#region # python包
import time

start_time = time.time()
print('开始导入包')
import sys, os, socket, threading, struct, json, re, serial, math, random, ast
# 获取当前文件的绝对路径
current_file_path = os.path.abspath(__file__)
# 获取当前文件所在的目录
current_dir = os.path.dirname(current_file_path)
# 获取上级目录
parent_dir = os.path.dirname(current_dir)
sys.path.append(parent_dir)
# import main.Ui_main
from widget import Ui_window_debug,Ui_widget_main,Ui_widget_config, Ui_widget_connect,Ui_widget_tags,Ui_widget_log,Ui_widget_clean,Ui_widget_ctu_up,Ui_widget_leds,Ui_widget_imu,Ui_widget_graphConfig,\
Ui_widget_mecanum,Ui_widget_servers,Ui_window_tool_convertMsg,Ui_window_handle
from PyQt5 import QtCore

print(QtCore.PYQT_VERSION_STR)
from PyQt5.QtGui import QImage, QPixmap, QPalette, QBrush, QPainter, QColor, QPen
# from PyQt5.QtCore import *
# from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import QApplication, QMainWindow, QColorDialog, QMessageBox, QPushButton, QSlider, QProgressBar, QHBoxLayout, QVBoxLayout, QWidget, QLabel, QLineEdit, QComboBox, QTabBar, QGridLayout, QCheckBox, QFileDialog, QRadioButton, QButtonGroup, qApp
from PyQt5.QtCore import QThread, pyqtSignal, QObject, Qt, QTimer, QTime, QStringListModel

import pyqtgraph as pg
from datetime import datetime
import numpy as np
from pynput import keyboard
# import cv2
# from pylibdmtx import pylibdmtx
from PyQt5.QtSerialPort import QSerialPortInfo
import openpyxl
# from openpyxl import Workbook
import qdarkstyle
from qdarkstyle.light.palette import LightPalette

#加密解密相关
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import base64

end_time = time.time()
elapsed_time = end_time - start_time
print("完成导入包", elapsed_time, "秒")
#endregion
#region # 定义全局变量
global_flag_client = 0  #socket连接标志位
global_s = 0  #socket，便于程序关闭时自动断开socket


#endregion
#region # 自定义函数
def makdir(directory):
    if not os.path.exists(directory):
        os.makedirs(directory)
        print(directory + "不存在,已创建")

    # else: print(directory+"已存在")


def extract_ip():  #获取本机IP
    st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        st.connect(('10.255.255.255', 1))
        IP = st.getsockname()[0]
    except Exception:
        IP = '127.0.0.1'
    finally:
        st.close()
    return IP


def get_time():  #获取本机时间
    current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
    return current_time


def HexStrAddSpace(HexStr):  #十六进制字符串标准化打印
    out_data = ''
    for i, s in enumerate(HexStr):
        if ((i % 2 == 0) & (i > 0)):
            out_data += ' '
            out_data += s
        else:
            out_data += s
    return out_data.upper()


def listToFloat(list):  #字符列表转小数
    return round(struct.unpack('<f', struct.pack('4B', *list))[0],
                 5)  #<表示小端字节序 f：这指定了要解析的数据类型为单精度浮点数（float）'4'：这表示接下来的格式字符将被重复四次。'B'：这是单个格式字符，表示无符号字符（unsigned char）。


    # return round(struct.unpack('>f', struct.pack('4B', *list))[0],5)
def listToFloat_B(list):  #字符列表转小数
    # return round(struct.unpack('<f', struct.pack('4B', *list))[0],5)#<表示小端字节序 f：这指定了要解析的数据类型为单精度浮点数（float）'4'：这表示接下来的格式字符将被重复四次。'B'：这是单个格式字符，表示无符号字符（unsigned char）。
    return round(struct.unpack('>f', struct.pack('4B', *list))[0], 5)


def floatToList(float_value):  #小数转字符列表
    # 将浮点数转换为二进制表示
    # binary = struct.pack('!f', float_value)#参数'!f'表示使用大端字节序
    binary = struct.pack('f', float_value)
    # 将二进制表示转换为十六进
    hex_array = [int(b) for b in binary]  #int整数表示  hex字符表示
    return hex_array


def floatToList_B(float_value):  #小数转字符列表_大端
    # 将浮点数转换为二进制表示
    binary = struct.pack('!f', float_value)  #参数'!f'表示使用大端字节序
    # binary = struct.pack('f', float_value)
    # 将二进制表示转换为十六进
    hex_array = [int(b) for b in binary]  #int整数表示  hex字符表示
    return hex_array


def listToUint16(list):  #字符列表转无符号32位整数
    return list[1] << 8 | list[0]


def listToInt16_big(list):  #字符列表转有符号32位整数
    if list[0] >> 7:
        return (list[0] << 8 | list[1]) - 0x10000
    else:
        return list[0] << 8 | list[1]


def listToInt16(list):  #字符列表转有符号32位整数
    if list[1] >> 7:
        return (list[1] << 8 | list[0]) - 0x10000
    else:
        return list[1] << 8 | list[0]


def listToInt32(arr):  #将数组转为32位带符号的整数
    # byte_obj = bytes(arr)
    # num = int.from_bytes(byte_obj, 'little', signed=True)
    num = int.from_bytes(arr, 'big', signed=True)
    return num


def listToUint32(arr):  #将数组转为32位无符号的整数
    num = int.from_bytes(arr, 'big', signed=False)
    return num


def listToInt32_little(arr):  #将数组转为32位带符号的整数
    # byte_obj = bytes(arr)
    # num = int.from_bytes(byte_obj, 'little', signed=True)
    num = int.from_bytes(arr, 'little', signed=True)
    return num


def int32Tolist_big(num):
    return [num >> 24 & 0xff, num >> 16 & 0xff, num >> 8 & 0xff, num & 0xff]


def int32Tolist(num):
    return [num & 0xff, num >> 8 & 0xff, num >> 16 & 0xff, num >> 24 & 0xff]


def int16Tolist_big(num):
    return [num >> 8 & 0xff, num & 0xff]


def int16Tolist(num):
    return [num & 0xff, num >> 8 & 0xff]


def SumCheck(data, length):  #按字节计算校验和
    checksum = 0
    for i in range(0, length):
        checksum += data[i]
        checksum &= 0xFF  # 强制截断
    return checksum


def clip_float(value, lower_bound, upper_bound):
    """
    将单个浮点数限制在 [lower_bound, upper_bound] 范围内。
 
    参数：
    - value: 需要限幅的浮点数。
    - lower_bound: 下限值。
    - upper_bound: 上限值。
 
    返回：
    - 限幅后的浮点数。
    """
    if value < lower_bound:
        return lower_bound
    elif value > upper_bound:
        return upper_bound
    else:
        return value


def get_exe_directory():
    # 检查是否是被打包后的环境
    if getattr(sys, 'frozen', False):
        # 如果是被打包后的环境，使用 sys.executable 获取可执行文件路径
        exe_path = sys.executable
    else:
        # 如果不是被打包后的环境，使用 __file__ 获取脚本路径
        exe_path = os.path.abspath(__file__)

    # 获取可执行文件所在的目录
    exe_directory = os.path.dirname(exe_path)
    return exe_directory


#endregion
#region # 自定义类
class QSSLoader:

    def __init__(self):
        pass

    @staticmethod
    def read_qss_file(qss_file_name):
        with open(qss_file_name, 'r', encoding='UTF-8') as file:
            return file.read()


class DeviceStatusChecker(QWidget):  #设备离线监测
    update_deviceStatus = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.last_heartbeat_time = QTime.currentTime()
        self.heartbeat_timer = QTimer()
        self.heartbeat_timer.timeout.connect(self.check_heartbeat)
        self.heartbeat_timer.start(1000)  # 1秒

    def check_heartbeat(self):
        current_time = QTime.currentTime()
        elapsed_time = self.last_heartbeat_time.msecsTo(current_time)
        # print(elapsed_time)
        if elapsed_time > 333:
            self.update_deviceStatus.emit("离线")
        else:
            self.update_deviceStatus.emit("在线")


class MyComboBox(QComboBox):  # 重写下拉框点(击时获取串口列表)

    def __init__(self, parent=None):
        super().__init__(parent)

    def showPopup(self):
        name = self.currentText()
        self.clear()
        serials = QSerialPortInfo.availablePorts()
        for com in serials:
            self.addItem(com.portName() + ' ' + com.description())
        super().showPopup()
        index = self.findText(name)
        if index >= 0:
            self.setCurrentIndex(index)


class sig_paintRoute(QObject):  # 定义信号
    sig_point = pyqtSignal(float, float)


class signalclass(QObject):  # 定义信号
    print = pyqtSignal(str)
    data_process = pyqtSignal(bytes)
    process_PGVData = pyqtSignal(bytes)
    # update_time=pyqtSignal(str)
    show_msg = pyqtSignal(str)
    pgB_DmCode = pyqtSignal(int)


class thread_debug(QThread):  # 监视AGV参数及绘制曲线
    sig_updateData = pyqtSignal(list)
    sig_updateData2 = pyqtSignal(list)
    sig_updateGraph = pyqtSignal(list, list)
    dataNum = 100  #曲线横坐标数据个数
    x = np.linspace(0, 100, dataNum)  # x轴数据

    p = [0] * 25
    p2 = [0] * 19
    # y1,y2,y3,y4,y5,y6=[0] * dataNum,[0] * dataNum,[0] * dataNum,[0] * dataNum,[0] * dataNum,[0] * dataNum
    graph_list = [[0] * 100 for _ in range(25)]

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(1)

    def fuc_updateData1(self, agv_data):
        # print(len(agv_data))
        for i in range(25):
            if i == 13 or i == 14 or i == 24:  #以int型解析
                datalist = agv_data[4 * i:4 * i + 4]
                self.p[i] = listToInt32(datalist)
            else:
                datalist = [agv_data[4 * i + 3], agv_data[4 * i + 2], agv_data[4 * i + 1], agv_data[4 * i]]
                self.p[i] = listToFloat(datalist)
            self.graph_list[i][:-1] = self.graph_list[i][1:]  # 数据向前移动一个位置
            self.graph_list[i][-1] = self.p[i]
            # self.p[i]=listToFloat(agv_data[4*i:4*i+4].reverse() )
        # agv_data=agv_data[self.num_4B*4:]
        # for i in range(self.num_2B):
        #     self.p[i+self.num_4B]=listToUint16(agv_data[2*i:2*i+2])
        # agv_data=agv_data[self.num_2B*2:]
        # for i in range(self.num_1B):
        #     self.p[i+self.num_4B+self.num_2B]=agv_data[i:i+1]

        self.sig_updateData.emit(self.p)
        self.sig_updateGraph.emit(self.x.tolist(), self.graph_list)

    def fuc_updateData2(self, agv_data):

        datalist = [agv_data[3], agv_data[2], agv_data[1], agv_data[0]]
        self.p2[0] = listToFloat(datalist)
        agv_data = agv_data[1 * 4:]  #4个字节的参数有1个
        print(len(agv_data))
        for i in range(2):
            #     # self.p2[i+1]=listToUint16(agv_data[2*i:2*i+2])
            self.p2[i + 1] = agv_data[2 * i:2 * i + 2]
            # print(self.p2[i+1])
            print(agv_data[2 * i:2 * i + 2])
        agv_data = agv_data[2 * 2:]  #2个字节的参数有2个
        # print(len(agv_data))
        for i in range(16):  #1个字节的参数有16个
            self.p2[i + 1 + 2] = agv_data[i:i + 1][0]
        print(type(agv_data[6:6 + 1][0]))
        self.sig_updateData2.emit(self.p2)


class thread_mecanum(QThread):  # 监测麦克纳姆轮数据
    sig_updateData = pyqtSignal(list, list, list)
    x = np.linspace(0, 100, 100)  # x轴数据
    p = [0] * 4
    data_list = [[0] * 100 for _ in range(4)]

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(1)

    def fuc_updateData(self, data):
        for i in range(4):
            self.p[i] = listToInt16_big(data[2 * i:2 * i + 2])
            self.data_list[i][:-1] = self.data_list[i][1:]  # 数据向前移动一个位置
            self.data_list[i][-1] = self.p[i]
        self.sig_updateData.emit(self.x.tolist(), self.data_list, self.p)


class thread_angle(QThread):  # 监测IMU原始数据
    sig_updateAngle = pyqtSignal(list, list)
    sig_updateImuRaw = pyqtSignal(list, list, list)
    dataNum = 100  #曲线横坐标数据个数
    dataNum_current = 0  #当前收到的第几个数据
    x = np.linspace(0, 100, dataNum)  # x轴数据
    y = np.random.rand(100)  # 随机生成一些初始数据
    # y1=y2=y3=y4=y5=y6=[0] * 100
    y = [0] * 6
    y1, y2, y3, y4, y5, y6 = [0] * dataNum, [0] * dataNum, [0] * dataNum, [0] * dataNum, [0] * dataNum, [0] * dataNum
    imuData_list = [y1, y2, y3, y4, y5, y6]

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(1)

    def fuc_updateAngle(self, agv_currentAngle):
        self.y[:-1] = self.y[1:]  # 数据向前移动一个位置
        self.y[-1] = agv_currentAngle

    def fuc_updateImuRaw(self, imuRawData):
        average, p = [0] * 6, [0] * 6
        if (self.dataNum_current >= self.dataNum):
            self.dataNum_current = self.dataNum - 1
            for i in range(6):
                p[i] = listToInt16(imuRawData[2 * i:2 * i + 2])  #gyroX,gyroY,gyroZ,accelX,accelY,accelZ
                self.imuData_list[i][:-1] = self.imuData_list[i][1:]  # 数据向前移动一个位置
                self.imuData_list[i][-1] = p[i]
        else:
            for i in range(6):
                p[i] = listToInt16(imuRawData[2 * i:2 * i + 2])  #gyroX,gyroY,gyroZ,accelX,accelY,accelZ
                self.imuData_list[i][self.dataNum_current] = p[i]

        self.dataNum_current += 1
        for i in range(6):
            self.y[i] = self.imuData_list[i][:(self.dataNum_current)]
            average[i] = np.mean(self.y[i])
            average[i] = np.round(average[i], 2)
        self.sig_updateImuRaw.emit(self.x[:self.dataNum_current].tolist(), self.y, average)


        # self.sig_updateImuRaw.emit(self.x.tolist(), self.imuData_list,average)
class thread_position(QThread):  # 定义进程
    update_position = pyqtSignal(list)
    last_new_point_x = 0
    last_new_point_y = 0
    dotdata = []

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(0.1)

    def update(self, new_point_x, new_point_y):
        if ((self.last_new_point_x != new_point_x) | (self.last_new_point_y != new_point_y)):
            newpoint = {'pos': (new_point_x, new_point_y)}
            if newpoint not in self.dotdata:  #点不存在则添加
                self.dotdata.append(newpoint)
            else:
                print("this point is exist")
            self.update_position.emit(self.dotdata)
        self.last_new_point_x = new_point_x
        self.last_new_point_y = new_point_y

    def clear(self):
        self.dotdata = []
        self.update_position.emit(self.dotdata)


class thread_feedback(QThread):  # 定义进程
    update_informaton = pyqtSignal(str, str, str, str)
    update_PGV_informaton = pyqtSignal(str, str, str, str)

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(0.1)

    def update(self, data):
        if ((data[0] == 0x40) & (data[1] == 0x01)):  #心跳包
            data = data[2:]
            agv_currentPosX = str(listToFloat(data[16:20]))  #世界坐标X信息
            agv_currentPosY = str(listToFloat(data[20:24]))  #世界坐标Y信息
            agv_currentAngle = str(listToFloat(data[24:28]))  #角度信息
            agv_ultrasonic = str(listToFloat(data[28:32]))  #超声波信息
            self.update_informaton.emit(agv_currentPosX, agv_currentPosY, agv_currentAngle, agv_ultrasonic)
        elif ((data[0] == 0x40) & (data[1] == 0x05)):  #更新PGV相机信息
            data = data[2:]
            # x
            PGV_posX = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
            if (data[0] >> 7): PGV_posX = PGV_posX - 0x100000000
            # y
            PGV_posY = data[4] << 8 | data[5]
            if (data[4] >> 7): PGV_posY = PGV_posY - 0x10000
            # w
            PGV_posYaw = data[6] << 8 | data[7]
            if (data[6] >> 7): PGV_posYaw = PGV_posYaw - 0x10000
            PGV_posYaw = round(PGV_posYaw / 10, 1)
            # number
            PGV_tagNuber = data[8] << 24 & 0xff | data[9] << 16 & 0xff | data[10] << 8 & 0xff | data[11] & 0xff
            self.update_PGV_informaton.emit(str(PGV_posX), str(PGV_posY), str(PGV_posYaw), str(PGV_tagNuber))


class thread_sendAudioFile(QThread):  # 进程_分包发送语音文件
    sig_sendAudioFile = pyqtSignal(list)
    sig_log = pyqtSignal(str)
    sig_next = pyqtSignal()
    sig_done = pyqtSignal()
    sig_packNum = pyqtSignal(int)

    def __init__(self, filename, index, next):
        super().__init__()
        self.filename = filename
        self.index = index
        self.next = next
        self.device_connect = 1

    def stop_send(self):
        self.device_connect = 0
        print("停止发送")

    def run(self):
        if self.filename:
            # print(self.filename)
            # print(self.index)
            with open(self.filename, 'rb') as f:
                data = f.read()
            fileLen = len(data)
            packNum = fileLen // 256 + 1
            self.sig_packNum.emit(packNum)
            leftLen = fileLen % 256
            # self.sig_log.emit("文件大小为%d个字节,%d个包,剩余%d个字节"%(fileLen,packNum,leftLen))
            frame_begin = [0x7E, 0xA0, 0x00, 0x03, self.index, packNum // 256, packNum % 256, 0xEF]
            if self.device_connect:
                self.sig_sendAudioFile.emit(frame_begin)
                print("开始下载第%d个文件" % self.index)
            else:
                print("结束发送进程")
            time.sleep(0.3)
            if self.device_connect:
                for i in range(1, packNum):
                    frame_data = [0x7E, 0xA1, i // 256, i % 256] + list(data[256 * (i - 1):256 * i]) + [0xEF]
                    if self.device_connect:
                        self.sig_sendAudioFile.emit(frame_data)
                    else:
                        break
                    time.sleep(0.06)
                frame_data = [0x7E, 0xA1, packNum // 256, packNum % 256] + list(data[256 * (packNum - 1):256 *
                                                                                     (packNum - 1) + leftLen]) + [0x00] * (256 - leftLen) + [0xEF]
                if self.device_connect:
                    self.sig_sendAudioFile.emit(frame_data)
                time.sleep(0.3)
                frame_end = [0x7E, 0xA3, 0x00, 0x02, 0x00, self.index, 0xEF]
                if self.device_connect:
                    self.sig_sendAudioFile.emit(frame_end)
                time.sleep(0.1)
                if self.next: self.sig_next.emit()
                else: self.sig_done.emit()
            else:
                print("结束发送进程")


class thread_sendBin(QThread):  # 进程_分包发送升级文件
    sig_sendBin = pyqtSignal(bytes)

    def __init__(self, app_address):
        super().__init__()
        self.app_address = app_address

    def run(self):
        self.iap_filename, _ = QFileDialog.getOpenFileName(None, 'Open File', self.app_address)
        with open(self.iap_filename, 'rb') as f:
            data = f.read()
        self.fileLen = len(data)
        packLen = 1024
        packNum = self.fileLen // packLen
        for i in range(packNum):
            sendData = data[i * packLen:(i + 1) * packLen]
            sendData2 = bytes(sendData)
            if i != 0 and i % 30 == 0: time.sleep(2)  #发送30K等待单片机存储到flash再发送
            self.sig_sendBin.emit(sendData2)
            time.sleep(0.2)
        if self.fileLen % packLen != 0:
            sendData = data[packNum * packLen:packNum * packLen + self.fileLen % packLen]
            sendData2 = bytes(sendData)
            self.sig_sendBin.emit(sendData2)
        print('发送完成')


class PaintRoute(QWidget):  # 运动轨迹绘制

    def __init__(self, parent=None):
        super().__init__(parent)
        self.sig_paintRoute = sig_paintRoute()
        #添加布局
        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.pw = pg.PlotWidget(title="AGV 运动轨迹", background='w')
        self.layout.addWidget(self.pw)
        # self.scatter = pg.ScatterPlotItem(size=10, brush= pg.mkBrush('g'),pen=pg.mkPen(color='g'))
        self.scatter = pg.ScatterPlotItem(
            size=10,
            brush=pg.mkBrush('g'),
            pen=pg.mkPen('g'),
            # pxMode=False,
            hoverable=True,
            hoverPen=pg.mkPen('g'),
            hoverBrush=pg.mkBrush('r'),
            hoverSize=10)
        self.pw.addItem(self.scatter)
        #设置图表
        self.pw.setXRange(-3, 3)
        self.pw.setYRange(-3, 3)
        self.pw.showGrid(x=True, y=True, alpha=0.5)

        #绘画示例
        self.dotdata = []  # 设置每个点的颜色
        # self.scatter.setData(self.dotdata)

        # self.pw.plot([5],[5],pen='g', brush=pg.mkBrush('r'),symbol='o')
        # self.pw.plot([1,2],[1,2],pen='b')
        # baseLine = pg.PolyLineROI([[0, 0], [1, 0], [1.5, 1], [2, 0], [3, 0]], pen=(0, 255, 0, 100), movable=False)
        # self.pw.addItem(baseLine)

        # 创建一个曲线项
        self.plot = self.pw.plot(
            symbol='o',
            pen='r',
        )
        # 初始化数据
        # self.data = np.random.rand(100)  # 随机生成一些初始数据
        # self.x = np.linspace(0, 10, 100)  # x轴数据
        self.x = []
        self.y = []
        # 更新数据并绘制曲线
        # self.update_plot()

        #鼠标事件
        self.setMouseTracking(False)
        self.plot.scene().sigMouseMoved.connect(self.mouseover)
        # self.plot.scene().sigMouseClicked.connect(self.mouse_clicked)
        self.scatter.sigClicked.connect(self.clicked)
        ## Make all plots clickable
        self.clickedPen = pg.mkPen('red', width=5)
        self.brush = pg.mkBrush(255, 0, 0, 120)
        self.lastClicked = []

    def clicked(self, plot, points):

        # for p in self.lastClicked:
        #     p.resetPen()
        # print("clicked points", p.pos())
        for p in points:
            p.setPen(self.clickedPen)
            p.setBrush(self.brush)
            print(p.data())
        self.lastClicked = points

    def update_plot(self):
        # 更新数据
        # self.data[:-1] = self.data[1:]  # 数据向前移动一个位置
        # self.data[-1] = np.random.rand()  # 在最后一个位置生成随机数

        # 更新曲线的数据
        self.plot.setData(self.x, self.data)

        # 设置曲线在x轴上的范围
        # self.pw.setXRange(self.x[0], self.x[-1])

        # 更新绘图
        self.pw.update()

        # 每隔一段时间调用update_plot函数来更新数据和绘图
        pg.QtCore.QTimer.singleShot(100, self.update_plot)

    def mouseover(self, pos):
        act_pos = self.plot.mapFromScene(pos)  # 参数pos 是像素坐标，需要 转化为  刻度坐标
        if type(act_pos) != QtCore.QPointF:
            return
        # print("over_1:",act_pos.x(), act_pos.y())
        self.new_point_x = round(act_pos.x(), 2)
        self.new_point_y = round(act_pos.y(), 2)

    def mouse_clicked(self, event):
        # if self.new_point_x not in self.x:
        #     self.x.append(self.new_point_x)
        # else:
        #     index = self.x.index(self.new_point_x)
        # if self.new_point_y not in self.y:
        #     self.y.append(self.new_point_y)

        # self.scatter.setData(self.dotdata)
        self.sig_paintRoute.sig_point.emit(self.new_point_x, self.new_point_y)


class AngleCurve(QWidget):  # 角度曲线绘制

    def __init__(self, parent=None):
        super().__init__(parent)
        #添加布局
        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.pw = pg.PlotWidget(title="AGV 角度曲线", background='w')
        self.pw.showGrid(x=True, y=True, alpha=0.5)
        self.layout.addWidget(self.pw)

        self.plot = self.pw.plot(
            symbol='o',
            symbolBrush='r',
            pen='r',
        )
        self.x = np.linspace(0, 10, 100)  # x轴数据
        self.y = np.random.rand(100)  # 随机生成一些初始数据
        self.plot.setData(self.x, self.y)
        self.plot1 = self.pw.plot(
            symbol='h',
            symbolBrush='b',
            pen='b',
        )
        self.y1 = [item + 2 for item in self.y]
        self.plot1.setData(self.x, self.y1)


class Inputor(QObject):  #输入指示灯
    INACTIVE = 0
    ACTIVE = 1

    def __init__(self, obj):
        super(Inputor, self).__init__()
        self._object = obj
        # print(obj)
        # self._size = obj.height()
        # print(self._size)
        self.inactive()

    def show(self, state):
        self._state = state
        colors = ['green', 'grey']  #低电平即0时代表有输入
        self._object.setStyleSheet("QPushButton{background-color:%s;border:0;border-radius: %dpx;}" % (colors[state], 16 / 2))

    def active(self):
        self._object.setStyleSheet("QPushButton{background-color:%s;border:0;border-radius: %dpx;}" % ('green', self._size / 2))

    def inactive(self):
        self._object.setStyleSheet("QPushButton{background-color:%s;border:0;border-radius: %dpx;}" % ('grey', 16 / 2))


class Indicator(QObject):  #指示灯
    """
        指示灯，支持显示指定的状态，或闪烁显示
    """
    INACTIVE = 0
    WARNING = 1
    ACTIVE = 2
    DISMISS = 3

    def __init__(self, obj, reverse=False):
        super(Indicator, self).__init__()
        self._object = obj
        self._size = obj.minimumHeight()
        self._timer = QTimer()
        self._timer.timeout.connect(self.__flashHandle)
        self._state = self.INACTIVE
        self._flashBaseState = self.DISMISS
        self._flashState = self.INACTIVE
        self._reverse = reverse
        self.show(self.INACTIVE)

    def show(self, state):
        self._state = state
        colors = ['red', 'orange', 'green', "rgb(240,240,240)"]
        if self._reverse:
            colors = ['green', 'orange', 'red', "rgb(240,240,240)"]
        self._object.setStyleSheet("QPushButton{background-color:%s;border:0;border-radius: %dpx;}" % (colors[state], self._size / 2))

    def active(self):
        self._timer.stop()
        self.show(self.ACTIVE)

    def inactive(self):
        self._timer.stop()
        self.show(self.INACTIVE)

    def warning(self):
        self._timer.stop()
        self.show(self.WARNING)

    def dismiss(self):
        self._timer.stop()
        self.show(self.DISMISS)

    def flash(self, state, period, baseState=DISMISS):
        if state == baseState:
            return
        self._flashState = state
        self._flashBaseState = baseState
        self.show(state)
        self._timer.start(period)

    def __flashHandle(self):
        if self._state == self._flashBaseState:
            self.show(self._flashState)
        else:
            self.show(self._flashBaseState)


class CustomRectWidget(QWidget):

    def __init__(self, shape):
        super().__init__()
        self.color_range = (0.3, 0.7)  # 初始颜色变化范围（20%到70%）
        self.setGeometry(50, 50, 300, 50)  # 设置位置和大小
        self.shape = shape
        if self.shape == "Horizontal":
            self.setMinimumSize(200, 20)
            self.setMaximumSize(200, 20)
        elif self.shape == "Vertical":
            self.setMinimumSize(20, 200)
            self.setMaximumSize(20, 200)

    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter(self)

        # 绘制整个矩形的背景色
        painter.setBrush(QBrush(QColor(220, 220, 220)))  # 浅灰色
        pen = QPen(QColor(200, 200, 200), 2)  # 黑色边框，宽度为 2
        painter.setPen(pen)
        print(self.rect())
        painter.drawRect(self.rect())

        # 计算颜色变化区域的宽度
        width = self.width()
        start_x = int(width * self.color_range[0])
        end_x = int(width * self.color_range[1])

        # 计算颜色变化区域的宽度
        height = self.height()
        start_y = int(height * self.color_range[0])
        end_y = int(height * self.color_range[1])

        # 绘制颜色变化区域
        if self.shape == "Horizontal":
            painter.setBrush(QBrush(QColor(0, 128, 0)))  # 绿色
            painter.setPen(QPen(QColor(0, 128, 0), 2))
            painter.drawRect(start_x, 0, end_x - start_x, self.height())
        elif self.shape == "Vertical":
            painter.setBrush(QBrush(QColor(0, 128, 0)))  # 绿色
            painter.setPen(QPen(QColor(0, 128, 0), 2))
            painter.drawRect(0, start_y, self.width(), end_y - start_y)

    def setColorRange(self, start, end):
        # 更新颜色变化范围并重新绘制控件
        self.color_range = (start, end)
        self.update()  # 触发重绘


class clientThread(QThread):
    # 定义信号来发送接收到的数据
    data_received = pyqtSignal(tuple, int, bytes)
    log = pyqtSignal(str)
    disConnect = pyqtSignal(int, QThread)

    def __init__(self, id, server_address):
        super().__init__()
        self.server_address = server_address
        self.identify = server_address.getpeername()
        self.running = True
        self.id = id

    def run(self):
        # current_thread = threading.current_thread()  #获取线程对象、名称，便于从线程列表中移除
        # sender = self.sender()
        # print(f"Running in thread: {self}")
        print(self.id)
        while self.running:
            try:
                data = self.server_address.recv(1024)
                if data:
                    self.data_received.emit(self.identify, self.id, data)
            except:
                self.log.emit(str(self.identify) + "接收异常")
                break
        self.disConnect.emit(self.id, self)
        self.server_address.close()

    def stop(self):
        self.running = False
        self.log.emit(str(self.identify) + '断开连接')
        self.server_address.close()

    def send_message(self, message):
        if self.server_address and self.server_address.fileno() != -1:
            try:
                self.server_address.sendall(message)
            except:
                # self.log.emit(f"Error sending message to {str(self.identify)}")
                self.log.emit(str(self.identify) + "已断开连接")
                self.disConnect.emit(self.id, self)
                self.server_address.close()
        else:
            self.log.emit(str(self.identify) + "连接不存在")


            # self.disConnect.emit(self.server_address)
class SocketWorker(QThread):
    # 定义信号来发送接收到的数据
    data_received = pyqtSignal(str)
    log = pyqtSignal(str)

    def __init__(self, server_address, client_socket=None):
        super().__init__()
        self.server_address = server_address
        self.client_socket = client_socket
        self.running = True

    def run(self):
        print('hlly')
        if not self.client_socket:
            try:
                self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.client_socket.connect(self.server_address)
                self.log.emit(str(self.server_address) + '连接成功')
                while self.running:
                    try:
                        data = self.client_socket.recv(1024).decode()
                        if data:
                            self.data_received.emit(data)
                    # except ConnectionResetError:
                    except:
                        print("Connection lost")
                        break
            except socket.error as e:
                self.log.emit(str(self.server_address) + str(e))
        self.client_socket.close()

    def send_message(self, message):
        if self.client_socket and self.client_socket.fileno() != -1:
            # if self.client_socket:
            try:
                self.client_socket.sendall(message.encode())
            except:
                print(f"Error sending message to {self.server_address}")
                # self.client_socket.fileno()
                # self.running = False
        else:
            print('连接已断开')

    def stop(self):
        self.running = False
        print('断开连接')
        # self.wait()


#region #
# class VideoThread(QThread):
#     change_pixmap_signal = pyqtSignal(QImage)
#     update_DM = pyqtSignal(str,int,int,int,int)

#     def __init__(self,rtmp_url):
#         super().__init__()
#         self.rtmp_url = rtmp_url
#         self.cap = cv2.VideoCapture(self.rtmp_url)
#         self.timer0_decode = QTimer()
#         self.timer0_decode.timeout.connect(self.fuc_decode)
#         # self.timer0_decode.start(1000)
#         self.ret=False
#         self.x, self.y, self.w, self.h = 0,0,0,0

#         # 相机识别子二维码像素坐标 到 二维码矩阵坐标 转换
#         cam_width=320#相机像素宽
#         cam_height=240#相机像素高
#         len_codes=85#二维码矩阵长宽为85mm
#         len_code=15#子二维码长宽为15mm
#         len_gap=5#两个子二维码中间间隙宽为15mm
#         scale_camHeight=1#相机镜头到二维码的高度影响二维码长宽占实际像素的大小
#         scale_camToPos=len_codes/cam_height*scale_camHeight
#         scale_posToCam=cam_height/len_codes*scale_camHeight

#         rows = 4
#         cols = 4
#         code_dis = [[0 for _ in range(cols)] for _ in range(rows)]
#         code_angle = [[0 for _ in range(cols)] for _ in range(rows)]
#         code_x = [[0 for _ in range(cols)] for _ in range(rows)]
#         code_y = [[0 for _ in range(cols)] for _ in range(rows)]
#         dis_xy=[-1.5*len_gap+-2*len_code,-0.5*len_gap-len_code,0.5*len_gap,1.5*len_gap+len_code]#相机相对于二维码为0°时,实际角度为90°时,子二维码起点到原点的x,y距离
#         angle=0#实际角度为90°,相机相对于二维码为0°;实际角度90°→0°，相机相对于二维码为0°°→90°
#         for i in range(rows):
#             for j in range(cols):
#                 code_dis[i][j]=math.hypot(dis_xy[i],dis_xy[j])
#                 code_angle[i][j]=math.atan2(dis_xy[i],dis_xy[j])/math.pi*180
#                 # print("第{}行{}列，距离{}".format(i,j,code_dis[i][j]))
#                 # print("第{}行{}列，距离{}，角度{}".format(i,j,code_dis[i][j],code_angle[i][j]))

#     def run(self):
#         self.exiting = False
#         print('开启摄像头')
#         self.cap = cv2.VideoCapture(self.rtmp_url)
#         if not self.cap.isOpened():
#             print("摄像头编号不存在，请检查摄像头连接或选择正确的摄像头编号。")
#         else:

#             print("摄像头信息：")
#             print("摄像头分辨率：", self.cap.get(cv2.CAP_PROP_FRAME_WIDTH), "x", self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
#             print("摄像头帧率：", self.cap.get(cv2.CAP_PROP_FPS))
#             print("摄像头是否打开：", self.cap.isOpened())
#             # self.timer0_decode.start(1000)
#             th2 = threading.Thread(target=self.fuc_decode)
#             th2.start()
#             while True:
#                 if self.exiting:
#                     self.cap.release()
#                     break
#                 try:
#                     self.ret, self.frame = self.cap.read()
#                 except Exception as e:
#                     print("发生错误：", e)
#                     break
#                 if self.ret:
#                     rgb_image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)

#                     # 在图像上绘制矩形
#                     cv2.rectangle(rgb_image, (self.x, self.y), (self.x + self.w, self.y + self.h), (0, 255, 0), 2)
#                     h, w, ch = rgb_image.shape
#                     bytes_per_line = ch * w
#                     convert_to_qt_format = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
#                     p = convert_to_qt_format.scaled(320, 240, aspectRatioMode=QtCore.Qt.KeepAspectRatio)
#                     self.change_pixmap_signal.emit(p)
#                     # all_barcode_info = pylibdmtx.decode(convert_to_qt_format, timeout=500, max_count=1)
#                     # print(all_barcode_info)
#                     # time.sleep(1)
#                     # del(ret)
#                     # del(frame)

#                 # print("hlly")

#     def stop(self):
#         self.exiting = True
#         self.ret=False
#         # self.timer0_decode.stop()
#         self.quit()
#         self.wait()
#         print("线程退出")
#     def fuc_decode(self):
#         # 保存图像到文件
#         # ret, frame = self.cap.read()
#         # print(ret)
#         # cv2.imwrite("captured_image.jpg", self.frame)
#         # 加载图片
#         # image = cv2.imread('captured_image.jpg')


#         # 解析二维码
#         while not self.exiting:
#             if self.ret:
#                 t0 = time.time()
#                 all_barcode_info = pylibdmtx.decode(self.frame, timeout=500, max_count=1)
#                 print(time.time() - t0)
#                 # print(all_barcode_info)
#                 # print(len(all_barcode_info))
#                 if len(all_barcode_info) == 1:
#                     # print(all_barcode_info[0][0][0])
#                     # print(all_barcode_info[0][0][1])
#                     # print(all_barcode_info[0][0][2])
#                     row=all_barcode_info[0][0][1]-48-2
#                     col=all_barcode_info[0][0][0]-48-2
#                     print(row,col)
#                     # print(all_barcode_info[0][1][0])
#                     # print(all_barcode_info[0][1][1])
#                     # 定义矩形的左上角坐标、宽度和高度
#                     self.x, self.y, self.w, self.h = all_barcode_info[0][1][0], 240-all_barcode_info[0][1][1], all_barcode_info[0][1][2], -all_barcode_info[0][1][3]
#                     # print(type(str(all_barcode_info[0][0])))
#                     witdth=math.hypot(self.w, self.h)
#                     print(witdth)
#                     self.update_DM.emit(all_barcode_info[0][0].decode('gbk'), self.x, self.y, self.w, self.h)
#                     # print(len(all_barcode_info[0][1]))
#                     # print(type(all_barcode_info[0][1]))
#                 time.sleep(1)
#endregion
class ServerThread(QThread):
    received_data = pyqtSignal(tuple, str)
    server_log = pyqtSignal(str)
    update_clients_list = pyqtSignal(str, tuple)
    stop_signal = pyqtSignal()
    signal_send_data = pyqtSignal(bytes)
    sig_data_process = pyqtSignal(bytes)
    sig_controlDevice_change = pyqtSignal(str)

    def __init__(self, host, port):
        super().__init__()
        self.host = host
        self.port = port
        self.client_sockets = []
        self.controlDevice = ''
        self.signal_send_data.connect(self.send_data)
        self.sig_controlDevice_change.connect(self.controlDevice_change)

    def run(self):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.bind((self.host, self.port))
        self.server_socket.listen(5)
        self.flag_run = True
        self.server_log.emit("服务器已启动，等待客户端连接...")

        while self.flag_run:
            try:
                client_socket, client_address = self.server_socket.accept()
                client_socket.settimeout(7)  # 设置超时时间为3秒
                self.server_log.emit(f"客户端 {client_address} 已连接")
                self.client_sockets.append(client_socket)
                self.update_clients_list.emit("add", client_address)  #更新界面列表
                threading.Thread(target=self.handle_client, args=(client_socket, client_address)).start()
            except:
                self.server_log.emit("服务器已关闭")
                # client_socket.close()
    def stop(self):
        self.flag_run = False
        self.server_socket.close()
        # 关闭所有连接设备
        # for i in range(0,len(self.client_sockets)):
        #     try:
        #         self.client_sockets[i].send(b"exit")
        #         self.client_sockets[i].close()
        #     except:
        #         break
        # self.client_sockets=[]
    def send_data(self, data):
        for i in range(0, len(self.client_sockets)):
            try:
                self.client_sockets[i].send(data)
            except:
                self.client_sockets.remove(self.client_sockets[i])
                self.server_log.emit(f"客户端 {self.client_sockets[i]} 已断开连接")

    def handle_client(self, client_socket, client_address):
        client_text = client_address[0] + ':' + str(client_address[1])
        while self.flag_run:
            try:
                data = client_socket.recv(1024)
                if not data:
                    break
                if (client_text == self.controlDevice):  #当前控制设备
                    self.sig_data_process.emit(data)
                else:  #其他设备
                    try:
                        self.received_data.emit(client_address, data.decode('utf-8'))
                    except Exception as e:
                        print(e)
            except Exception as e:
                print(f"客户端 {client_address} 断开连接，原因： {e}")
                break
        client_socket.close()
        self.server_log.emit(f"客户端 {client_address} 断开连接")
        self.client_sockets.remove(client_socket)
        self.update_clients_list.emit("remove", client_address)  #更新界面列表

    def controlDevice_change(self, device):
        self.controlDevice = device
        print(self.controlDevice)


#endregion
#region # 自定义窗口
class Window_tool_convertMsg(QMainWindow, Ui_window_tool_convertMsg.Ui_MainWindow):  #调度协议转换窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.btn_convert.clicked.connect(self.fuc_convert)
        self.btn_clear.clicked.connect(self.fuc_clear)

    def fuc_convert(self):
        text = self.text_raw.toPlainText()
        arr = text.split(" ")
        msgHead = ''.join([chr(int(x, 16)) for x in arr[:24]])
        msgLen = int(msgHead[4:9])
        msgCmd = ''
        for j in range(10):
            cmd = arr[24 + 32 * j:24 + 32 + 32 * j]
            cmdNum = int(cmd[0], 16) << 8 | int(cmd[1], 16)
            if cmdNum:
                cmdName = ''.join([chr(int(x, 16)) for x in cmd[2:4]])
                cmd_p = []
                for i in range(7):
                    cmd_pi = listToInt32([int(x, 16) for x in cmd[4 * i + 4:4 * i + 8]])
                    cmd_p.append(cmd_pi)
                msgCmd += str(cmdNum) + " " + cmdName + " " + str(cmd_p)
        print(msgCmd)
        if self.check_showTime.isChecked():
            msgCmd = get_time() + "  " + msgCmd
        self.text_convert.append(msgCmd)

    def fuc_clear(self):
        self.text_convert.setText('')


class Window_debug(QMainWindow, Ui_window_debug.Ui_MainWindow):  #详细参数监测窗口
    sig_close = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.setupUi(self)

    def closeEvent(self, event):
        # 在这里添加你想要在关闭窗口时执行的操作
        self.sig_close.emit()

        # event.accept()


class Window_handle(QMainWindow, Ui_window_handle.Ui_MainWindow):  #手柄

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_config(QWidget, Ui_widget_config.Ui_Form):  #配置窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_tags(QWidget, Ui_widget_tags.Ui_Form):  #二维码标定

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_clean(QWidget, Ui_widget_clean.Ui_Form):  #清洗机

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_ctu_up(QWidget, Ui_widget_ctu_up.Ui_Form):  #CTU上装

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_leds(QWidget, Ui_widget_leds.Ui_Form):  #LED

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_log(QWidget, Ui_widget_log.Ui_Form):  #日志窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_connect(QWidget, Ui_widget_connect.Ui_Form):  #连接窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_graphConfig(QWidget, Ui_widget_graphConfig.Ui_Form):  #曲线图配置

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_mecanum(QWidget, Ui_widget_mecanum.Ui_Mecanum):  #麦克纳姆轮小车调试

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.plot_widget = pg.GraphicsLayoutWidget()
        self.layout_plot.addWidget(self.plot_widget)
        self.plot_item1 = self.plot_widget.addPlot()
        self.plot_item1.addLegend(size=(150, 80))
        # self.plot_item1.showGrid(x=True, y=True, alpha=0.5)#网格透明度
        self.curve_Va = self.plot_item1.plot(pen='r', name='Va')  #symbol='o',,,gyroZ,accelX,accelY,accelZ
        self.curve_Vb = self.plot_item1.plot(pen='g', name='Vb')  #绿色 (Green)
        self.curve_Vc = self.plot_item1.plot(pen='b', name='Vc')  #,symbol='o'蓝色 (Blue)
        self.curve_Vd = self.plot_item1.plot(pen='m', name='Vd')  #,symbol='o'品红 (Magenta)
        self.curve = [self.curve_Va, self.curve_Vb, self.curve_Vc, self.curve_Vd]


class widget_imu(QWidget, Ui_widget_imu.Ui_Form):  #IMU曲线

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.average = [
            self.input_average_gyroX, self.input_average_gyroY, self.input_average_gyroZ, self.input_average_accX, self.input_average_accY,
            self.input_average_accZ
        ]
        # 创建一个GraphicsLayoutWidget对象
        self.plot_widget = pg.GraphicsLayoutWidget()
        self.layout_plot.addWidget(self.plot_widget)

        # 在GraphicsLayoutWidget中添加一个PlotItem
        self.plot_item1 = self.plot_widget.addPlot()
        self.plot_widget.nextRow()
        self.plot_item2 = self.plot_widget.addPlot()

        # 生成数据
        self.x = np.linspace(0, 100, 100)
        self.y = 1000 * np.sin(self.x)
        self.y2 = 1000 * np.sin(self.x) + 2
        self.imudata = []
        # 绘制曲线图
        self.plot_item1.addLegend(size=(150, 80))
        self.plot_item2.addLegend(size=(150, 80))
        # self.plot_item1.showGrid(x=True, y=True, alpha=0.5)#网格透明度
        self.curve_gyroX = self.plot_item1.plot(pen='r', name='gyroX')  #symbol='o',,,gyroZ,accelX,accelY,accelZ
        self.curve_gyroY = self.plot_item1.plot(pen='g', name='gyroY')  #绿色 (Green)
        self.curve_gyroZ = self.plot_item1.plot(pen='b', name='gyroZ')  #,symbol='o'蓝色 (Blue)
        self.curve_accX = self.plot_item2.plot(pen='c', name='accelX')  #青色 (Cyan)
        self.curve_accY = self.plot_item2.plot(pen='m', name='accelY')  #品红 (Magenta)
        self.curve_accZ = self.plot_item2.plot(pen='y', name='accelZ')  #黄色 (Yellow)
        self.curve = [self.curve_gyroX, self.curve_gyroY, self.curve_gyroZ, self.curve_accX, self.curve_accY, self.curve_accZ]


class widget_servers(QWidget, Ui_widget_servers.Ui_Form):  #多服务器连接

    def __init__(self):
        super().__init__()
        self.setupUi(self)

    #     self.btn_connect.clicked.connect(self.connect_server)
    # def connect_server(self):
    #     print('hlly')


class window_picture(QMainWindow):  #纯背景图片窗口

    def __init__(self):
        super().__init__()

        # self.setWindowOpacity(0.5)
        # 设置窗口无边框
        self.setWindowFlags(Qt.FramelessWindowHint)
        # 设置窗口尺寸为宽度400像素，高度300像素
        # self.setFixedSize(1787, 954)
        self.setFixedSize(1450, 860)
        # 设置窗口背景图片
        palette = QPalette()
        # pixmap = QPixmap(r'E:\python\board.png')
        pixmap = QPixmap(r'E:\python\background.jpg')
        pixmap = pixmap.scaled(self.size(), Qt.KeepAspectRatioByExpanding)
        brush = QBrush(pixmap)
        # 设置背景图片

        # self.setAutoFillBackground(True)
        palette.setBrush(QPalette.Background, brush)
        self.setPalette(palette)
        # # # 创建一个布局并将QLabel添加到布局中
        layout = QVBoxLayout()
        # # layout.addWidget(self.label)

        # # # 创建一个QWidget对象并将布局设置为其布局
        container = QWidget()
        container.setLayout(layout)

        # # # 将QWidget设置为主窗口的中心窗口
        self.setCentralWidget(container)


#endregion


class Window_main(QMainWindow, Ui_widget_main.Ui_MainWindow):  #主窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # self.setFixedSize(1450, 860)
        # self.setWindowFlag(QtCore.Qt.FramelessWindowHint) # 隐藏边框
        self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)  # 设置获取焦点的方法
        self.widget_connect = widget_connect()
        self.widget_log = widget_log()
        #region #
        #endregion
        #region # 按键监听线程
        self.keyIsPressed = False
        self.AppIsFocus = False
        listener = keyboard.Listener(on_press=self.on_press, on_release=self.on_release)
        listener.start()
        #endregion
        #region # 设置窗口大小
        size = self.size()
        width = size.width()
        height = size.height()
        # self.resize(1920, 1080)
        # self.showNormal()
        # self.resize(800, 600)
        # print(f'窗口宽度： {width}, 窗口高度： {height}')
        #endregion
        #region # 定义信号
        self.signal = signalclass()
        self.signal.show_msg[str].connect(self.sigCall_show_msg)
        self.signal.print[str].connect(self.sigCall_print)
        self.signal.pgB_DmCode.connect(self.sigCall_pgB_DmCode)
        self.signal.data_process.connect(self.fuc_data_process)
        #endregion
        #region # 定义进程
        self.thread_angle = thread_angle()
        self.thread_angle.sig_updateAngle.connect(self.sigCall_update_angle)
        self.thread_angle.sig_updateImuRaw.connect(self.sigCall_update_imuRawData)
        self.thread_angle.start()
        self.thread_position = thread_position()
        self.thread_position.update_position.connect(self.sigCall_update_position)
        self.thread_position.start()
        self.thread_feedback = thread_feedback()
        self.thread_feedback.update_informaton.connect(self.sigCall_update_infomation)
        self.thread_feedback.update_PGV_informaton.connect(self.sigCall_update_PGV_informaton)
        self.thread_feedback.start()
        self.thread_debug = thread_debug()
        self.thread_debug.sig_updateData.connect(self.sigCall_update_debug)
        self.thread_debug.sig_updateData2.connect(self.sigCall_update_debug2)
        self.thread_debug.sig_updateGraph.connect(self.sigCall_update_debugGraph)
        self.thread_debug.start()
        self.thread_mecanum = thread_mecanum()
        self.thread_mecanum.sig_updateData.connect(self.sigCall_update_mecanum)
        self.thread_mecanum.start()
        self.deviceStatusChecker = DeviceStatusChecker()
        self.deviceStatusChecker.update_deviceStatus.connect(self.sigCall_update_deviceStatus)
        #endregion
        #region # 定义定时器
        # self.timer0_deviceState = QTimer()
        # self.timer0_deviceState.timeout.connect(self.fuc_update_deviceState)
        # self.timer0_deviceState.start(1000)
        # self.timer1_agv_stop = QTimer()
        # self.timer1_agv_stop.timeout.connect(self.fuc_agv_autoStop)
        # self.timer2_agv_moveCycle = QTimer()
        # self.timer2_agv_moveCycle.timeout.connect(self.fuc_agv_cycleMove)
        # self.timer3_camera_query = QTimer()
        # self.timer3_camera_query.timeout.connect(self.fuc_camera_query)
        # self.timer4_imu_query = QTimer()
        # self.timer4_imu_query.timeout.connect(lambda:self.fuc_imu_config("queryAngle"))
        # self.timer5_agv_angleJudge = QTimer()
        # self.timer5_agv_angleJudge.timeout.connect(self.fuc_agv_angleJudge)
        # self.timer6_agv_odometer = QTimer()
        # self.timer6_agv_odometer.timeout.connect(self.fuc_agv_odometer)
        # self.timer7_agv_toDistance = QTimer()
        # self.timer7_agv_toDistance.timeout.connect(self.fuc_agv_toDistance)
        # self.timer8_agv_anologAngle = QTimer()
        # self.timer8_agv_anologAngle.timeout.connect(self.fuc_agv_anologAngle)
        self.timer9_playPoints = QTimer()
        self.timer9_playPoints.timeout.connect(self.task_playPoints)
        self.timer10_imuTest = QTimer()
        self.timer10_imuTest.timeout.connect(self.task_imuTest)
        # self.timer11_update_mapPoints = QTimer()
        # self.timer11_update_mapPoints.timeout.connect(self.task_update_mapPoints)
        # 创建一个定时器用于定时更新图像
        # self.timer12 = QTimer(self)
        # self.timer12.timeout.connect(self.update_frame)

        # self.timerTest = QTimer(self)
        # self.timerTest.timeout.connect(self.timer_Test)
        # self.timerTest.start(1000)

        self.timer13 = QTimer(self)
        self.timer13.timeout.connect(self.resizeWindow)
        self.timer13.start(50)  #重置窗口大小
        self.timer14_decode = QTimer()
        self.timer14_decode.timeout.connect(self.fuc_decode)
        # self.timer14_decode.start(1000)
        #endregion
        #region # 默认配置页面
        # self.add_tab("人员权限")# 添加标签页
        # self.add_tab("设置")
        # self.tab_widget.removeTab(0)
        # self.tab_widget.addTab(self.tabBar, 'title')
        self.tab_config = QWidget()
        layout = QVBoxLayout()
        self.widget_config = widget_config()
        layout.addWidget(self.widget_config)
        self.tab_config.setLayout(layout)
        # self.tab_widget.addTab(self.tab_config, '启动参数配置')
        self.widget_config.btn_config_save.clicked.connect(self.fuc_config_save)
        self.widget_config.btn_config_load.clicked.connect(self.fuc_config_load)
        self.widget_config.btn_config_set.clicked.connect(self.fuc_config_set)
        self.widget_config.btn_config_restart.clicked.connect(self.restart)

        # self.fuc_log(f"当前 exe 所在文件夹路径: {parent_dir_path}")
        file_path = parent_dir_path + "/appDoc/config/default.txt"
        # self.fuc_log(file_path)

        if os.path.exists(file_path):
            # print('文件存在')
            with open(file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
                for line in lines:
                    if '#' in line:
                        line = line[:line.index('#')]  #去除每行#后的内容
                    if line.strip():  #去除字符串两端的空白字符（包括空格、换行符和制表符）
                        parameter = line.strip().split(":", 1)
                        search_string = parameter[0]
                        found_widget = self.widget_config.findChild(QLineEdit, search_string)  #根据字符串查找widget中的对象
                        if found_widget:
                            found_widget.setText(parameter[1])
                        else:
                            found_widget = self.widget_config.findChild(QComboBox, search_string)  #根据字符串查找widget中的对象
                            if found_widget:
                                found_widget.setCurrentText(parameter[1])  #必须是下拉列表中的已有项
                            else:
                                found_widget = self.widget_config.findChild(QCheckBox, search_string)
                                found_widget.setChecked(True if parameter[1] == 'True' else False)
        else:
            print('文件不存在')
            dir_path = parent_dir_path + "/appDoc/config"
            if not os.path.exists(dir_path):  # 创建目录
                os.makedirs(dir_path)
            with open(file_path, "w") as file:  # 创建文件
                pass

        file_path = parent_dir_path + "/appDoc/config/serverIpPort.txt"
        if os.path.exists(file_path):
            with open(file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            if lines == []:
                print('文件无内容')
            else:
                self.widget_connect.com_server.clear()
                for line in lines:
                    if '#' in line:
                        line = line[:line.index('#')]  #去除每行#后的内容
                    if line.strip():  #去除字符串两端的空白字符（包括空格、换行符和制表符）
                        Ip = line.strip()
                        # print(Ip)
                        self.widget_connect.com_server.addItem(Ip)
                        # parameter=line.strip().split(":", 1)
                        # self.widget_connect.com_server.addItem(parameter[0])
                # print(lines)
        else:
            print('文件不存在')
            dir_path = parent_dir_path + "/appDoc/config"
            if not os.path.exists(dir_path):  # 创建目录
                os.makedirs(dir_path)
            with open(file_path, "w") as file:  # 创建文件
                pass
        #endregion
        #region # 参数配置_上位机
        self.data_from_board = 1
        p_serverIP = self.widget_config.p_serverIP.text()
        p_serverPort = self.widget_config.p_serverPort.text()
        if p_serverIP == '': p_serverIP = "10.0.0.78"
        p_protocol = self.widget_config.p_protocol.currentText()
        if p_protocol == '': p_protocol = "My"
        p_page_index = int(self.widget_config.p_page_index.currentText().split(":")[0])

        self.widget_connect.com_protocol.setCurrentText(p_protocol)
        self.protocol = p_protocol  #默认通讯协议
        self.widget_connect.com_server.setCurrentText(p_serverIP)
        self.widget_connect.comboBox_serverPort.setCurrentText(p_serverPort)
        self.stackedWidget.setCurrentIndex(p_page_index)  #初始化显示第几个界面,[2:远程升级 6:调度]
        self.app_address = self.widget_config.p_address_bin.text()  #远程升级文件地址
        if self.app_address == "":
            self.app_address = parent_dir_path

        p_lineSpeed = self.widget_config.p_lineSpeed.text()
        self.input_agv_lineSpeed.setText(p_lineSpeed)
        p_rateSpeed = self.widget_config.p_rateSpeed.text()
        self.input_agv_rateSpeed.setText(p_rateSpeed)
        self.widget_connect.com_protocol.currentIndexChanged.connect(self.change_protocol)
        self.widget_connect.com_protocol_1.currentIndexChanged.connect(self.change_protocol)
        self.cnt_deviceState = 0
        self.last_agv_currentPosX = 1
        self.last_agv_currentPosY = 1

        # self.mode_debug="客户端"
        self.mode_debug = self.widget_config.p_mode_debug.currentText()
        self.action_mode_uart.triggered.connect(self.change_debugMode)  #选择调试模式
        self.action_mode_client.triggered.connect(self.change_debugMode)
        self.action_mode_server.triggered.connect(self.change_debugMode)
        self.action_config.triggered.connect(self.show_widget_config)
        if self.mode_debug != "串口": self.hide_all_widgets(self.widget_connect.gridLayout_com)
        if self.mode_debug != "服务器": self.hide_all_widgets(self.widget_connect.gridLayout_server)
        if self.mode_debug != "客户端": self.hide_all_widgets(self.widget_connect.gridLayout_client)
        #endregion
        #region # 打开摄像头
        self.flag_camera = 0
        self.disply_width = 640
        self.display_height = 480
        self.label_video.resize(self.disply_width, self.display_height)
        # self.video_thread = VideoThread('rtmp://127.0.0.1:1935/live/123')
        # self.video_thread = VideoThread('rtsp://127.0.0.1:8554/123')

        # self.video_thread = VideoThread(0)
        # self.video_thread.change_pixmap_signal.connect(self.update_image)
        # self.video_thread.update_DM.connect(self.update_DM)

        # self.cap = cv2.VideoCapture(1)
        # if not self.cap.isOpened():
        #     print("摄像头编号不存在，请检查摄像头连接或选择正确的摄像头编号。")
        # else:
        #     print("摄像头信息：")
        #     print("摄像头分辨率：", self.cap.get(cv2.CAP_PROP_FRAME_WIDTH), "x", self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        #     print("摄像头帧率：", self.cap.get(cv2.CAP_PROP_FPS))
        #     print("摄像头是否打开：", self.cap.isOpened())
        #endregion
        #region # 子界面/界面显示切换
        #分割器
        self.splitter.setSizes([100, 900])
        self.splitter_2.setSizes([300, 300])

        # TAB选项卡
        self.tabBar = self.tab_widget.findChild(QTabBar)
        # self.tabBar.hide()  #主页标签显示控制
        # self.tab_widget.tabBarDoubleClicked.connect(self.changeTabName) # 双击设置选项卡名称
        # self.tab_widget.tabCloseRequested.connect(self.deltab)
        self.tab_widget.tabCloseRequested.connect(self.close_tab)

        # 绘图窗口
        self.paint = PaintRoute()  #运动轨迹窗口
        self.layout_route.addWidget(self.paint)
        self.paint.sig_paintRoute.sig_point.connect(self.thread_position.update)
        self.angleCurve = AngleCurve()  #陀螺仪角度曲线
        self.layout_angleCurve.addWidget(self.angleCurve)

        # 侧边栏
        # self.bar_left.currentRowChanged.connect(lambda:self.fuc_change_stack("设备控制"))
        # self.bar_left.currentRowChanged.connect(self.fuc_change_stack)
        self.bar_left.itemClicked.connect(self.fuc_change_stack)
        self.tab_widget.currentChanged.connect(self.on_tab_changed)
        # self.tab_widget.currentChanged.connect(self.fuc_change_stack)
        # print(self.stackedWidget.currentIndex())

        #附属（日志区、地图区、视频区）
        self.action_log.toggled.connect(self.toggle_area_log)
        self.action_map.toggled.connect(self.toggle_area_map)
        self.action_video.toggled.connect(self.toggle_area_video)
        self.toggle_area_video(False)
        self.hide_all_widgets(self.horizontalLayout_3)

        #独立窗口
        self.action_watch.toggled.connect(self.toggle_window_debug)
        self.action_mecanum.toggled.connect(self.toggle_widget_mecanum)
        # self.action_servers.toggled.connect(self.toggle_widget_servers)
        self.action_servers.triggered.connect(self.toggle_widget_servers)
        self.action_handle.triggered.connect(self.toggle_widget_handle)

        #endregion
        #region # 麦克纳姆轮小车调试
        self.widget_mecanum = widget_mecanum()
        # self.widget_mecanum.show()
        # self.widget_mecanum.S_setPWM_LF.valueChanged.connect(self.fuc_change_wheel_LF)

        self.widget_mecanum.S_setPWM_LF.sliderReleased.connect(self.mecanum_setPWM)
        self.widget_mecanum.S_setPWM_RF.sliderReleased.connect(self.mecanum_setPWM)
        self.widget_mecanum.S_setPWM_LB.sliderReleased.connect(self.mecanum_setPWM)
        self.widget_mecanum.S_setPWM_RB.sliderReleased.connect(self.mecanum_setPWM)
        self.widget_mecanum.S_setSpeed_LF.sliderReleased.connect(self.mecanum_setRPM)
        self.widget_mecanum.S_setSpeed_RF.sliderReleased.connect(self.mecanum_setRPM)
        self.widget_mecanum.S_setSpeed_LB.sliderReleased.connect(self.mecanum_setRPM)
        self.widget_mecanum.S_setSpeed_RB.sliderReleased.connect(self.mecanum_setRPM)
        # self.widget_mecanum.input_setPWM_LB_set.textChanged.connect(self.fuc_change_wheel_pwm1)
        self.mecanum_pwm = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        self.widget_mecanum.input_setPWM_LF.returnPressed.connect(lambda: self.mecanum_setPWM1('LF'))
        self.widget_mecanum.input_setPWM_RF.returnPressed.connect(lambda: self.mecanum_setPWM1('RF'))
        self.widget_mecanum.input_setPWM_LB.returnPressed.connect(lambda: self.mecanum_setPWM1('LB'))
        self.widget_mecanum.input_setPWM_RB.returnPressed.connect(lambda: self.mecanum_setPWM1('RB'))
        self.widget_mecanum.input_setSpeed_LF.returnPressed.connect(self.mecanum_setRPM1)
        self.widget_mecanum.input_setSpeed_RF.returnPressed.connect(self.mecanum_setRPM1)
        self.widget_mecanum.input_setSpeed_LB.returnPressed.connect(self.mecanum_setRPM1)
        self.widget_mecanum.input_setSpeed_RB.returnPressed.connect(self.mecanum_setRPM1)
        self.widget_mecanum.input_LF_Kp.returnPressed.connect(self.mecanum_pid_set)
        self.widget_mecanum.btn_stop.clicked.connect(self.mecanum_stop)

        self.pid_inputs = [
            self.widget_mecanum.input_LF_Kp,
            self.widget_mecanum.input_LF_Ki,
            self.widget_mecanum.input_LF_Kd,
            self.widget_mecanum.input_RF_Kp,
            self.widget_mecanum.input_RF_Ki,
            self.widget_mecanum.input_RF_Kd,
            self.widget_mecanum.input_LB_Kp,
            self.widget_mecanum.input_LB_Ki,
            self.widget_mecanum.input_LB_Kd,
            self.widget_mecanum.input_RB_Kp,
            self.widget_mecanum.input_RB_Ki,
            self.widget_mecanum.input_RB_Kd,
        ]
        self.widget_mecanum.btn_pid_read.clicked.connect(self.mecanum_pid_read)
        self.widget_mecanum.btn_pid_set.clicked.connect(self.mecanum_pid_set)
        self.widget_mecanum.btn_pid_save.clicked.connect(self.mecanum_pid_save)
        self.widget_mecanum.btn_pid_load.clicked.connect(self.mecanum_pid_load)
        self.widget_mecanum.input_maxSpeed.returnPressed.connect(self.mecanum_setMaxspeed)
        self.mecanum_setMaxspeed()  #设置速度滑动条的范围
        self.widget_mecanum.check_mode_control.toggled.connect(self.mecanum_mode_control)

        # self.widget_mecanum.S_speedX.sliderReleased.connect(self.mecanum_setSpeed)
        # self.widget_mecanum.input_speedX.returnPressed.connect(self.mecanum_setSpeed1)
        self.widget_mecanum.btn_forward.clicked.connect(self.mecanum_forward)
        self.widget_mecanum.btn_back.clicked.connect(self.mecanum_back)

        self.widget_mecanum.S_speedX.sliderReleased.connect(self.mecanum_setSpeedXYZ)
        self.widget_mecanum.S_speedY.sliderReleased.connect(self.mecanum_setSpeedXYZ)
        self.widget_mecanum.S_speedZ.sliderReleased.connect(self.mecanum_setSpeedXYZ)
        self.widget_mecanum.input_speedX.returnPressed.connect(self.mecanum_setSpeedXYZ1)
        self.widget_mecanum.input_speedY.returnPressed.connect(self.mecanum_setSpeedXYZ1)
        self.widget_mecanum.input_speedZ.returnPressed.connect(self.mecanum_setSpeedXYZ1)
        #endregion
        #region # AGV手柄
        self.flag_calibrate_scale = 0
        self.widget_handle = Window_handle()
        self.widget_handle.btn_calibrate_begin.clicked.connect(self.handle_calibrate_begin)
        self.widget_handle.btn_calibrate_end.clicked.connect(self.handle_calibrate_end)
        # self.custom_rect1 = CustomRectWidget("Horizontal")
        # self.widget_handle.horizontalLayout.addWidget(self.custom_rect1)
        # self.custom_rect2 = CustomRectWidget("Horizontal")
        # self.widget_handle.horizontalLayout_2.addWidget(self.custom_rect2)
        # self.custom_rect3 = CustomRectWidget("Vertical")
        # self.widget_handle.horizontalLayout_3.addStretch(1)
        # self.widget_handle.horizontalLayout_3.addWidget(self.custom_rect3)
        # self.widget_handle.horizontalLayout_3.addStretch(1)
        # self.custom_rect4 = CustomRectWidget("Vertical")
        # self.widget_handle.horizontalLayout_4.addStretch(1)
        # self.widget_handle.horizontalLayout_4.addWidget(self.custom_rect4)
        # self.widget_handle.horizontalLayout_4.addStretch(1)
        #endregion
        #region # 串口配置
        # 初始化serial对象 用于串口通信
        self.ser = serial.Serial()
        # 初始化端口列表
        self.portList = MyComboBox()
        self.widget_connect.gridLayout_com.addWidget(self.portList, 0, 1, 1, 1)  #添加到几行几列，并占据几行几列的位置
        if self.mode_debug != "串口": self.portList.setVisible(False)
        # 打开/关闭串口 按钮
        self.widget_connect.btn_com.clicked.connect(self.fuc_OpenCom)
        self.rxflag = 0
        #endregion
        #region # 服务器控制（作为服务器）
        # self.hostip=extract_ip()
        # print(self.hostip)
        self.hostip = '10.0.0.78'
        self.widget_connect.input_hostip.setText(self.hostip + ':5000')
        self.widget_connect.btn_server.clicked.connect(self.start_server)
        self.flag_server = False
        self.server_thread = ServerThread(self.hostip, 5000)
        # self.server_thread.received_data.connect(self.show_received_data)
        # self.server_thread.server_log.connect(self.fuc_log)
        # self.server_thread.update_clients_list.connect(self.update_clients_list)
        self.widget_log.btn_clear.clicked.connect(self.fuc_clear)
        self.controlDevice = ''
        self.widget_connect.com_clients.currentIndexChanged.connect(self.controlDevice_change)  #切换控制设备
        self.server_thread.sig_data_process.connect(self.fuc_data_process)  #接收数据处理
        #endregion
        #region # 连接/断开 服务器 (作为客户端)
        self.widget_connect.check_mode_wifiToUart.toggled.connect(self.fuc_check_mode_wifiToUart)
        self.widget_connect.com_server.currentIndexChanged.connect(self.fuc_com_server_change)
        #endregion
        #region # 连接多个服务器 (作为客户端)
        self.widget_servers = widget_servers()
        self.servers_checkBoxs = []
        self.servers_inputs = []
        self.servers_labels = []
        self.servers_buttons = []
        self.servers_sliders = []
        self.servers_pgBars = []
        self.servers_thread = []
        # 指定目录和文件名
        directory = parent_dir_path + "/appDoc/list_server/"  # 替换为你的目标目录
        filename = 'default.xlsx'
        # 构建完整的文件路径
        file_path = os.path.join(directory, filename)
        # 确保目录存在
        if not os.path.exists(directory):
            os.makedirs(directory)
            print('创建目录')
        if os.path.exists(file_path):
            print('文件存在')
            try:
                # 选择工作表
                workbook = openpyxl.load_workbook(file_path)
                sheet = workbook.active
                label = QLabel('升级进度')
                self.widget_servers.gridLayout.addWidget(label, 0, 5)
                # print(sheet.max_row)
                for row in range(1, sheet.max_row):
                    # print(sheet.cell(row+1,2).value)
                    check = QCheckBox()
                    check.setChecked(True)
                    self.servers_checkBoxs.append(check)
                    self.widget_servers.gridLayout.addWidget(check, row, 0)

                    for j in range(2):
                        line_edit = QLineEdit(self)
                        line_edit.setText(str(sheet.cell(row + 1, 1 + j).value))
                        self.servers_inputs.append(line_edit)
                        self.widget_servers.gridLayout.addWidget(line_edit, row, 1 + j)

                    label = QLabel('未连接')
                    self.servers_labels.append(label)
                    self.widget_servers.gridLayout.addWidget(label, row, 3)

                    button = QPushButton("连接")
                    button.clicked.connect(lambda _, index=row - 1: self.servers_connect(index))
                    self.servers_buttons.append(button)
                    self.widget_servers.gridLayout.addWidget(button, row, 4)

                    # slider = QSlider(Qt.Horizontal)
                    # slider.setRange(0, 100)
                    # slider.sliderReleased.connect(lambda index=row-1:self.servers_updateProgress(index))
                    # self.servers_sliders.append(slider)
                    # self.widget_servers.gridLayout.addWidget(slider,row,5)

                    pgBar = QProgressBar()
                    # pgBar.sliderReleased.connect(lambda index=row-1:self.servers_updateProgress(index))
                    self.servers_pgBars.append(pgBar)
                    self.widget_servers.gridLayout.addWidget(pgBar, row, 5)
                # 关闭Excel文件
                workbook.close()
            except:
                self.fuc_log('服务器IP文件加载错误')
        else:
            print('文件不存在')
            wb = openpyxl.Workbook()
            wb.save(file_path)
        # filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path+"/testDoc/test", "Excel Files (*.xlsx);;All Files (*)")
        # workbook = openpyxl.load_workbook(filename)

        self.widget_servers.btn_connect.clicked.connect(self.servers_connectAll)
        self.widget_servers.btn_disConnect.clicked.connect(self.servers_disConnectAll)
        self.widget_servers.check_all.toggled.connect(self.servers_chooseAll)
        #endregion
        #region # 日志管理
        data_today = datetime.now().strftime('20%y-%m-%d')
        dir_name = 'appDoc/log'
        if not os.path.isdir(dir_name):
            os.makedirs(dir_name)
        self.widget_log.input_record_name.setText(data_today)
        self.widget_log.btn_openlog.clicked.connect(self.fuc_openlog)
        #endregion
        #region # AGV运动控制
        self.btn_agv_stop.clicked.connect(self.fuc_agv_stop)  #停止
        self.btn_agv_moveForward.clicked.connect(self.fuc_agv_moveForward)  #前进
        self.btn_agv_moveBack.clicked.connect(self.fuc_agv_moveBack)  #后退
        self.btn_agv_rotateCW.clicked.connect(self.fuc_agv_rotateCW)  #正转
        self.btn_agv_rotateCCW.clicked.connect(self.fuc_agv_rotateCCW)  #反转
        self.btn_agv_run.clicked.connect(self.fuc_agv_run)  #曲线运动
        self.btn_agv_runBack.clicked.connect(self.fuc_agv_runBack)  #反向曲线运动
        self.btn_agv_toAngle.clicked.connect(self.fuc_agv_toAngle)  #旋转到指定角度
        self.btn_agv_toDistance.clicked.connect(self.fuc_agv_toDistance)  #运行指定距离
        self.btn_agv_toPos.clicked.connect(self.fuc_agv_toPos)  #运行到指定点
        self.btn_agv_resetPos.clicked.connect(self.fuc_agv_resetPos)  #位置坐标复位
        self.btn_agv_resetAngle.clicked.connect(self.fuc_agv_resetAngle)  #角度复位
        self.check_obstacle_ignore.toggled.connect(self.fuc_obstacle_ignore)  #配置雷达避障
        self.check_watch_agvState.toggled.connect(self.fuc_watch_agvState)  #AGV状态监测开启/关闭
        self.checkBox_enMotor.toggled.connect(self.fuc_enMotor)
        # self.btn_enMotor.clicked.connect(self.fuc_enMotor)
        # self.btn_motor_protect.clicked.connect(self.fuc_motor_protect)
        #endregion
        #region # 转盘及升降机构控制
        self.btn_rotator_CW.clicked.connect(self.fuc_rotator_CW)
        self.btn_rotator_CCW.clicked.connect(self.fuc_rotator_CCW)
        self.btn_rotator_cw.clicked.connect(self.fuc_rotator_CW)
        self.btn_rotator_ccw.clicked.connect(self.fuc_rotator_CCW)
        self.btn_liftor_up.clicked.connect(self.fuc_liftor_up)
        self.btn_liftor_down.clicked.connect(self.fuc_liftor_down)
        self.btn_liftor_down1.clicked.connect(lambda: self.fuc_liftor_upDown(0))
        self.btn_liftor_up1.clicked.connect(lambda: self.fuc_liftor_upDown(1))
        self.btn_stop_RU.clicked.connect(self.fuc_stop_RU)
        self.btn_motorT_mode.clicked.connect(self.fuc_motorT_mode)
        self.btn_motorU_mode.clicked.connect(self.fuc_motorU_mode)
        self.btn_motorT_setOrigin.clicked.connect(self.fuc_motorT_setOrigin)
        self.btn_motorU_setOrigin.clicked.connect(self.fuc_motorU_setOrigin)
        self.input_motorT_position_T.returnPressed.connect(self.fuc_motorT_position_T)
        self.input_motorU_position_T.returnPressed.connect(self.fuc_motorU_position_T)
        self.input_motorT_angle_T.returnPressed.connect(self.fuc_motorT_angle_T)
        self.input_motorU_height_T.returnPressed.connect(self.fuc_motorU_height_T)
        self.btn_motor_remote_stop.clicked.connect(self.fuc_motor_disconnect)
        self.check_motor_communicate.toggled.connect(self.fuc_motor_communicate)
        self.check_rotator_RR.toggled.connect(self.fuc_rotator_RR)
        #endregion
        #region # 运动轨迹
        dir_name = 'appDoc/trajectory'
        if not os.path.isdir(dir_name):
            os.makedirs(dir_name)
        # self.paint.dotdata=[{'pos': [-676.1, 0.0]},{'pos': [0, 0]}]#初始化轨迹点
        self.paint.scatter.setData(self.paint.dotdata)
        self.dotdata = []
        self.btn_clear_points.clicked.connect(self.fuc_clear_points)
        self.btn_save_points.clicked.connect(self.fuc_saveTrajectory)
        self.btn_load_points.clicked.connect(self.fuc_loadTrajectory)
        self.btn_play_points.clicked.connect(lambda: self.fuc_playTrajectory(1))
        self.btn_playback_points.clicked.connect(lambda: self.fuc_playTrajectory(0))
        self.btn_save_keyPoints.clicked.connect(self.fuc_save_keyDot)
        self.btn_load_keyPoints.clicked.connect(self.fuc_load_keyDot)

        self.keyDots = []  #关键点
        #endregion
        #region # 多点路径规划
        self.check_pointCycle.clicked.connect(self.fuc_pointCycle)
        self.btn_startPointRecycle.clicked.connect(self.fuc_startPointRecycle)
        #endregion
        #region # 轨迹跟踪参数
        self.btn_trace_query.clicked.connect(self.fuc_trace_query)
        self.btn_trace_set.clicked.connect(self.fuc_trace_set)
        #endregion
        #region # AGV参数配置
        dir_name = 'appDoc/parameter'
        if not os.path.isdir(dir_name):
            os.makedirs(dir_name)
        self.btn_system_sleep.clicked.connect(self.fuc_system_sleep)
        self.btn_system_restart.clicked.connect(self.fuc_system_restart)
        self.btn_system_reset.clicked.connect(self.fuc_system_reset)
        self.btn_software_reset.clicked.connect(self.fuc_software_reset)
        self.btn_rtc_init.clicked.connect(self.fuc_rtc_init)
        self.btn_eeprom_init.clicked.connect(self.fuc_eeprom_init)
        self.btn_activeChip.clicked.connect(self.fuc_activeChip)
        self.btn_read_flashParameter.clicked.connect(self.fuc_read_flashParameter)
        self.btn_set_flashParameter.clicked.connect(self.fuc_set_flashParameter)
        self.btn_save_flashParameter.clicked.connect(self.fuc_save_flashParameter)
        self.btn_save_parameter.clicked.connect(self.fuc_save_parameter)
        self.btn_load_paramete.clicked.connect(self.fuc_load_parameter)
        self.agv_parameter = [
            'input_software_version',
            'input_scale_rateToLine',
            'input_agv_default_lineSpeed',
            'input_agv_default_rateSpeed',
            'input_agv_max_lineSpeed',
            'input_agv_max_rateSpeed',
            'input_agv_acceleration',
            'input_agv_deceleration',
            'input_agv_RateAcceleration',
            'input_agv_RateDeceleration',
            'input_distanceRangeOffset',
            'input_angleRangeOffset',
            'input_LW_compensation',
            'input_RW_compensation',
            'input_LW_offset',
            'input_RW_offset',
            'input_factor_lineSpeed',
            'input_factor_rotateSpeed',
            'input_factor_rectify',
            'input_rectifyAngle',
            'input_imu_integralGain',
            'input_imu_offsetX',
            'input_imu_offsetY',
            'input_imu_offsetZ',
        ]
        #endregion
        #region # AGV程序升级
        self.btn_iap_in.clicked.connect(self.fuc_iap_in)  #进入bootloader
        self.btn_app_in.clicked.connect(self.fuc_app_in)  #进入APP（取消升级）
        self.btn_iap_sendLen.clicked.connect(self.fuc_iap_sendLen)  #发送固件长度
        self.btn_receive_in.clicked.connect(self.fuc_receive_in)  #进入接收固件状态
        self.btn_iap_sendBin.clicked.connect(self.fuc_iap_sendBin)  #8266\串口升级
        self.btn_updateProgram.clicked.connect(self.fuc_updateProgram)  #一键升级
        self.iap_filename = ''
        self.packLen = 512

        if self.input_app_address.text() == '':
            self.input_app_address.setText(self.app_address)
        #endregion
        #region # 校准IMU（陀螺仪）
        self.flag_imu_test = 0
        self.btn_imu_testCW.clicked.connect(lambda: self.fuc_imu_test(1))
        self.btn_imu_testCCW.clicked.connect(lambda: self.fuc_imu_test(0))
        self.btn_imu_test.clicked.connect(lambda: self.fuc_imu_test(2))

        #标签页
        self.widget_imu = widget_imu()
        self.widget_imu.slider_rateSpeed.valueChanged.connect(self.fuc_set_rateSpeed)
        self.widget_imu.btn_rateSpeed.clicked.connect(self.fuc_reset_rateSpeed)
        self.widget_imu.input_rateSpeed.returnPressed.connect(self.fuc_set1_rateSpeed)
        self.widget_imu.btn_reset_curve.clicked.connect(self.fuc_reset_curve)
        self.widget_imu.btn_offset_calibration.clicked.connect(self.fuc_offset_calibration)
        # self.tab_imu = QWidget()
        # layout = QVBoxLayout()
        # layout.addWidget(self.widget_imu)
        # self.tab_imu.setLayout(layout)
        #endregion
        #region # 地图二维码
        self.pushButton_load_map.clicked.connect(self.fuc_load_map_DmCode)
        #endregion
        #region # 二维码标定
        # 创建标签页
        self.page_codeNum = 50  #每页显示的二维码个数
        self.widget_tags = widget_tags()

        # self.tab_widget.addTab(self.tab_tags, '二维码标定')
        # self.btn_query_DmCode.clicked.connect(self.fuc_query_DmCode)
        # self.btn_set_DmCode.clicked.connect(self.fuc_set_DmCode)
        # self.btn_DmCode_next.clicked.connect(self.fuc_page_next)
        # self.btn_DmCode_pre.clicked.connect(self.fuc_page_pre)
        # self.btn_save_DmCode.clicked.connect(self.fuc_save_DmCode)
        # self.btn_load_DmCode.clicked.connect(self.fuc_load_DmCode)
        # self.btn_clear_DmCode.clicked.connect(self.fuc_clear_Dmcode)
        self.widget_tags.btn_tags_query.clicked.connect(self.fuc_query_DmCode)
        self.widget_tags.btn_tags_set.clicked.connect(self.fuc_set_DmCode)
        self.widget_tags.btn_tags_save.clicked.connect(self.fuc_save_DmCode)
        self.widget_tags.btn_tags_load.clicked.connect(self.fuc_load_DmCode)
        self.widget_tags.btn_page_pre.clicked.connect(self.fuc_page_pre)
        self.widget_tags.btn_page_next.clicked.connect(self.fuc_page_next)
        self.widget_tags.input_page_current.returnPressed.connect(self.fuc_page_jump)
        self.widget_tags.check_all.toggled.connect(self.fuc_check_DmCode)
        #endregion
        #region # AGV
        self.layout_log.addWidget(self.widget_log)
        self.layout_connect.addWidget(self.widget_connect)
        #endregion
        #region # 清洗机
        self.widget_clean = widget_clean()
        # self.widget_clean.layout_connect.addWidget(self.widget_connect)
        # self.widget_clean.layout_log.addWidget(self.widget_log)
        self.widget_clean.checkBox_scrub.toggled.connect(self.fuc_scrub)
        self.widget_clean.checkBox_water_valve.toggled.connect(self.fuc_water_valve)
        self.widget_clean.checkBox_sewage_valve.toggled.connect(self.fuc_sewage_valve)

        self.widget_clean.pushButton_scrub_stop.clicked.connect(lambda: self.fuc_pushRod(0x01, 0x00))  #刷盘01，刮耙02， 00：停止 01：上升  02：下降
        self.widget_clean.pushButton_scrub_up.clicked.connect(lambda: self.fuc_pushRod(0x01, 0x01))
        self.widget_clean.pushButton_scrub_down.clicked.connect(lambda: self.fuc_pushRod(0x01, 0x02))
        self.widget_clean.pushButton_scraper_stop.clicked.connect(lambda: self.fuc_pushRod(0x02, 0x00))
        self.widget_clean.pushButton_scraper_up.clicked.connect(lambda: self.fuc_pushRod(0x02, 0x01))
        self.widget_clean.pushButton_scraper_down.clicked.connect(lambda: self.fuc_pushRod(0x02, 0x02))

        self.widget_clean.horizontalSlider_vacuum.sliderReleased.connect(self.fuc_vacuum)
        self.widget_clean.horizontalSlider_water_pump.sliderReleased.connect(self.fuc_water_pump)
        self.widget_clean.lineEdit_vacuum.returnPressed.connect(self.fuc_vacuum_input)
        self.widget_clean.lineEdit_water_pump.returnPressed.connect(self.fuc_water_pump_input)
        self.widget_clean.pushButton_clean_stop.clicked.connect(lambda: self.fuc_clean_move(0x00))
        self.widget_clean.pushButton_clean_forward.clicked.connect(lambda: self.fuc_clean_move(0x01))
        self.widget_clean.pushButton_clean_backward.clicked.connect(lambda: self.fuc_clean_move(0x02))
        self.widget_clean.pushButton_clean_turnLeft.clicked.connect(lambda: self.fuc_clean_move(0x03))
        self.widget_clean.pushButton_clean_turnRight.clicked.connect(lambda: self.fuc_clean_move(0x04))

        #endregion
        #region # 清洗机工作站
        self.widget_clean.checkBox_station_led_charge.toggled.connect(lambda: self.clean_output_control(0x01))
        self.widget_clean.checkBox_station_led_water.toggled.connect(lambda: self.clean_output_control(0x02))
        self.widget_clean.checkBox_station_led_water2.toggled.connect(lambda: self.clean_output_control(0x03))
        self.widget_clean.checkBox_station_led_clean.toggled.connect(lambda: self.clean_output_control(0x04))
        self.widget_clean.checkBox_station_led_green.toggled.connect(lambda: self.clean_output_control(0x05))
        self.widget_clean.checkBox_station_led_red.toggled.connect(lambda: self.clean_output_control(0x06))
        self.widget_clean.checkBox_station_led_yellow.toggled.connect(lambda: self.clean_output_control(0x07))
        self.widget_clean.checkBox_station_out_extend.toggled.connect(lambda: self.clean_output_control(0x08))
        self.widget_clean.checkBox_station_out_retract.toggled.connect(lambda: self.clean_output_control(0x09))
        self.widget_clean.checkBox_station_valve_clean.toggled.connect(lambda: self.clean_output_control(0x0A))
        self.widget_clean.checkBox_station_valve_water.toggled.connect(lambda: self.clean_output_control(0x0B))
        #endregion
        #region # CTU
        self.widget_ctu_up = widget_ctu_up()
        #设置默认位置
        file_path = parent_dir_path + "/appDoc/config/location.txt"
        locations = []
        if os.path.exists(file_path):
            with open(file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            if lines == []:
                print('文件无内容')
            else:
                for line in lines:
                    if '#' in line:
                        line = line[:line.index('#')]  #去除每行#后的内容
                    if line.strip():  #去除字符串两端的空白字符（包括空格、换行符和制表符）
                        location = line.strip().split(":")[1]
                        locations.append(location)
        else:
            print('文件不存在')
        # print(locations)
        self.widget_ctu_up.input_motor2_position_T.setText(locations[0])
        self.widget_ctu_up.input_motor3_position_T.setText(locations[1])
        self.widget_ctu_up.input_motor4_position_T.setText(locations[2])
        self.widget_ctu_up.input_motor5_position_T.setText(locations[3])
        #使能控制
        self.widget_ctu_up.checkBox_en_motor2.toggled.connect(lambda: self.ctu_motor_en(0x02))
        self.widget_ctu_up.checkBox_en_motor3.toggled.connect(lambda: self.ctu_motor_en(0x03))
        self.widget_ctu_up.checkBox_en_motor4.toggled.connect(lambda: self.ctu_motor_en(0x04))
        self.widget_ctu_up.checkBox_en_motor5.toggled.connect(lambda: self.ctu_motor_en(0x05))
        #模式设置
        self.widget_ctu_up.btn_motor2_mode.clicked.connect(lambda: self.ctu_motor_mode(0x02))
        self.widget_ctu_up.btn_motor3_mode.clicked.connect(lambda: self.ctu_motor_mode(0x03))
        self.widget_ctu_up.btn_motor4_mode.clicked.connect(lambda: self.ctu_motor_mode(0x04))
        self.widget_ctu_up.btn_motor5_mode.clicked.connect(lambda: self.ctu_motor_mode(0x05))
        #原点标定
        self.widget_ctu_up.btn_motor2_setOrigin.clicked.connect(lambda: self.ctu_motor_setOrigin(0x02))
        self.widget_ctu_up.btn_motor3_setOrigin.clicked.connect(lambda: self.ctu_motor_setOrigin(0x03))
        self.widget_ctu_up.btn_motor4_setOrigin.clicked.connect(lambda: self.ctu_motor_setOrigin(0x04))
        self.widget_ctu_up.btn_motor5_setOrigin.clicked.connect(lambda: self.ctu_motor_setOrigin(0x05))
        #电机运行到指定位置
        self.widget_ctu_up.input_motor2_position_T.returnPressed.connect(lambda: self.ctu_motor_position_T(0x02))
        self.widget_ctu_up.input_motor3_position_T.returnPressed.connect(lambda: self.ctu_motor_position_T(0x03))
        self.widget_ctu_up.input_motor4_position_T.returnPressed.connect(lambda: self.ctu_motor_position_T(0x04))
        self.widget_ctu_up.input_motor5_position_T.returnPressed.connect(lambda: self.ctu_motor_position_T(0x05))
        #机构运行到指定位置
        self.widget_ctu_up.input_motor2_location_T.returnPressed.connect(lambda: self.ctu_device_location_T(0x02))
        self.widget_ctu_up.input_motor3_location_T.returnPressed.connect(lambda: self.ctu_device_location_T(0x03))
        self.widget_ctu_up.input_motor4_location_T.returnPressed.connect(lambda: self.ctu_device_location_T(0x04))
        self.widget_ctu_up.input_motor5_location_T.returnPressed.connect(lambda: self.ctu_device_location_T(0x05))
        #电机以指定速度运行
        self.widget_ctu_up.btn_motor2_speed0.clicked.connect(lambda: self.ctu_motor_control(0x02, 0))
        self.widget_ctu_up.btn_motor2_speed1.clicked.connect(lambda: self.ctu_motor_control(0x02, 1))
        self.widget_ctu_up.btn_motor2_speed2.clicked.connect(lambda: self.ctu_motor_control(0x02, 2))
        self.widget_ctu_up.btn_motor3_speed0.clicked.connect(lambda: self.ctu_motor_control(0x03, 0))
        self.widget_ctu_up.btn_motor3_speed1.clicked.connect(lambda: self.ctu_motor_control(0x03, 1))
        self.widget_ctu_up.btn_motor3_speed2.clicked.connect(lambda: self.ctu_motor_control(0x03, 2))
        self.widget_ctu_up.btn_motor4_speed0.clicked.connect(lambda: self.ctu_motor_control(0x04, 0))
        self.widget_ctu_up.btn_motor4_speed1.clicked.connect(lambda: self.ctu_motor_control(0x04, 1))
        self.widget_ctu_up.btn_motor4_speed2.clicked.connect(lambda: self.ctu_motor_control(0x04, 2))
        self.widget_ctu_up.btn_motor5_speed0.clicked.connect(lambda: self.ctu_motor_control(0x05, 0))
        self.widget_ctu_up.btn_motor5_speed1.clicked.connect(lambda: self.ctu_motor_control(0x05, 1))
        self.widget_ctu_up.btn_motor5_speed2.clicked.connect(lambda: self.ctu_motor_control(0x05, 2))
        #拨杆电机控制
        self.widget_ctu_up.checkBox_paddle.toggled.connect(lambda: self.ctu_device_control(0x06))
        # self.widget_ctu_up.pushButton_motor6_open.clicked.connect(lambda: self.ctu_device_control(0x06, 1))
        # self.widget_ctu_up.pushButton_motor6_close.clicked.connect(lambda: self.ctu_device_control(0x06, 0))
        #输出控制
        self.widget_ctu_up.checkBox_out_liftST.toggled.connect(lambda: self.ctu_output_control(0x01))
        self.widget_ctu_up.checkBox_out_rotateST.toggled.connect(lambda: self.ctu_output_control(0x02))

        self.widget_ctu_up.checkBox_out_liftPWR.toggled.connect(lambda: self.ctu_output_control(0x01))
        self.widget_ctu_up.checkBox_out_walkPWR.toggled.connect(lambda: self.ctu_output_control(0x02))
        self.widget_ctu_up.checkBox_out_radarF1.toggled.connect(lambda: self.ctu_output_control(0x03))
        self.widget_ctu_up.checkBox_out_radarF2.toggled.connect(lambda: self.ctu_output_control(0x04))
        self.widget_ctu_up.checkBox_out_radarF3.toggled.connect(lambda: self.ctu_output_control(0x05))
        self.widget_ctu_up.checkBox_out_radarF4.toggled.connect(lambda: self.ctu_output_control(0x06))
        self.widget_ctu_up.checkBox_out_radarB1.toggled.connect(lambda: self.ctu_output_control(0x07))
        self.widget_ctu_up.checkBox_out_radarB2.toggled.connect(lambda: self.ctu_output_control(0x08))
        self.widget_ctu_up.checkBox_out_radarB3.toggled.connect(lambda: self.ctu_output_control(0x09))
        self.widget_ctu_up.checkBox_out_radarB4.toggled.connect(lambda: self.ctu_output_control(0x0A))
        self.widget_ctu_up.checkBox_out_sto.toggled.connect(lambda: self.ctu_output_control(0x0B))
        self.widget_ctu_up.checkBox_out_ledLR.toggled.connect(lambda: self.ctu_output_control(0x0D))
        self.widget_ctu_up.checkBox_out_ledLG.toggled.connect(lambda: self.ctu_output_control(0x0E))
        self.widget_ctu_up.checkBox_out_ledLY.toggled.connect(lambda: self.ctu_output_control(0x0F))
        self.widget_ctu_up.checkBox_out_ledRR.toggled.connect(lambda: self.ctu_output_control(0x10))
        self.widget_ctu_up.checkBox_out_ledRG.toggled.connect(lambda: self.ctu_output_control(0x11))
        self.widget_ctu_up.checkBox_out_ledRY.toggled.connect(lambda: self.ctu_output_control(0x12))
        #组合控制
        self.widget_ctu_up.pushButton_excecute_goods.clicked.connect(self.ctu_excecute_goods)
        self.widget_ctu_up.pushButton_emergencyStop.clicked.connect(self.ctu_emergencyStop)
        self.widget_ctu_up.pushButton_reset.clicked.connect(self.ctu_reset)
        #endregion
        #region # LED
        self.widget_leds = widget_leds()
        self.widget_leds.lineEdit_led_num.returnPressed.connect(self.led_num)
        self.widget_leds.comboBox_led_mode.clear()
        self.widget_leds.comboBox_led_mode.addItems(['常亮', '闪烁', '流水', '呼吸', '渐变'])
        self.widget_leds.comboBox_led_mode.currentIndexChanged.connect(self.led_mode)
        self.widget_leds.comboBox_led_color.clear()
        self.widget_leds.comboBox_led_color.addItems(['灭', '红', '橙', '黄', '绿', '蓝', '靛', '紫', '白'])
        self.widget_leds.comboBox_led_color.currentIndexChanged.connect(self.led_color)
        self.widget_leds.horizontalSlider_led_light.sliderReleased.connect(self.led_light)
        self.widget_leds.lineEdit_led_light.returnPressed.connect(self.led_light_input)
        self.widget_leds.horizontalSlider_led_speed.sliderReleased.connect(self.led_speed)
        self.widget_leds.lineEdit_led_speed.returnPressed.connect(self.led_speed_input)
        #endregion
        #region # 串口设置
        #endregion
        #region # 调度模拟
        self.S_cmd_default = []
        self.cmdNum = 0  #指令序号0-9999
        self.count_cmdDone = 0
        check = QCheckBox()
        check.toggled.connect(self.fuc_check_cmd_all)
        self.layout_cmd.addWidget(check, 0, 0, 1, 1)
        textlist = ['指令码', '指令名', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7']  #['指令码','指令名','P1','P2','P3','P4','P5','P6','P7','状态']
        self.cmd_column = len(textlist) + 1
        for i in range(len(textlist)):  #创建第一行标签
            label = QLabel(textlist[i])
            # label.setObjectName(f'label_{i+1}')
            self.layout_cmd.addWidget(label, 0, i + 1, 1, 1)
        num_cmd = 10
        self.num_cmd = 10
        for i in range(1):  #创建复选框  #i为所在列，j为0-10行
            for j in range(num_cmd):
                check = QCheckBox()
                self.layout_cmd.addWidget(check, j + 1, 0, 1, 1)
        for i in range(1):  #创建输入框
            for j in range(num_cmd):
                line_edit = QLineEdit()
                self.layout_cmd.addWidget(line_edit, j + 1, i + 1, 1, 1)
        for i in range(1):  #创建下拉框
            for j in range(num_cmd):
                com = QComboBox()
                self.layout_cmd.addWidget(com, j + 1, i + 2, 1, 1)
        for i in range(7):  #创建输入框
            for j in range(num_cmd):
                line_edit = QLineEdit()
                self.layout_cmd.addWidget(line_edit, j + 1, i + 3, 1, 1)
        # for i in range(1):#创建标签
        #     for j in range(num_cmd):
        #         label = QLabel('未开始')
        #         self.layout_cmd.addWidget(label, j+1, i+10, 1, 1)
        cmdDic = {'01': 'ST', '02': 'GP', "03": 'SP', "04": 'RV', "05": 'RA', "06": 'HL', "07": 'SL', "08": 'CF'}
        # for i in range(10):#指令码赋初值
        #     widget = self.layout_cmd.itemAt(11+(1)*num_cmd+i).widget()
        #     widget.clear()
        #     widget.addItems(list(cmdDic.keys()))
        # widget.currentIndexChanged.connect(self.fuc_cmdNumChange)
        for i in range(10):  #指令名赋初值
            widget = self.layout_cmd.itemAt(self.cmd_column + (2) * num_cmd + i).widget()
            widget.clear()
            widget.addItems(list(cmdDic.values()))

            # widget.currentIndexChanged.connect(self.fuc_cmdNameChange)
        for i in range(10):  #复选框勾选时添加命令号
            widget = self.layout_cmd.itemAt(self.cmd_column + (0) * num_cmd + i).widget()
            widget.stateChanged.connect(self.fuc_change_cmdNum)
        self.btn_cmd_save.clicked.connect(self.fuc_cmdSave)
        self.btn_cmd_load.clicked.connect(self.fuc_cmdLoad)
        self.btn_sendCmd.clicked.connect(self.fuc_sendCmd_TCS)
        self.check_switchAuthority.toggled.connect(self.fuc_switchAuthority)
        # widget = self.layout_cmd.itemAt(11+3*num_cmd+0).widget()#第一行有十一个参数，每列有10个命令
        # widget.setText('已开始')
        # widget = self.layout_cmd.itemAt(11+(3+6)*num_cmd+9).widget()
        # widget.setText('已开始')
        # for i in range(self.layout_cmd.count()):#打印对象名称
        # widget = self.layout_cmd.itemAt(i).widget()
        # print(widget.objectName())

        #endregion
        #region # 参数配置_上位机2
        tabname = self.widget_config.p_tabwidget_id.currentText()
        if tabname == '二维码标定':
            self.tab_widget.addTab(self.widget_tags, tabname)
        elif tabname == '陀螺仪数据':
            self.tab_widget.addTab(self.widget_imu, tabname)
        elif tabname == '清洗机':
            self.widget_clean.layout_connect.addWidget(self.widget_connect)
            self.widget_clean.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_clean, tabname)
        elif tabname == "CTU":
            self.widget_ctu_up.layout_connect.addWidget(self.widget_connect)
            self.widget_ctu_up.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_ctu_up, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "LED":
            self.widget_leds.layout_connect.addWidget(self.widget_connect)
            self.widget_leds.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_leds, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        p_tabwidget_id = self.get_index_fromTabname(tabname)
        self.tab_widget.setCurrentIndex(p_tabwidget_id)  #显示标签页
        #endregion
        #region # AGV内部参数监测界面
        self.window_debug = Window_debug()
        self.window_debug.dataColor = []  #默认颜色

        self.window_debug.plot_widget = pg.GraphicsLayoutWidget()  #详细参数监测曲线
        self.window_debug.plot_widget.setMinimumHeight(500)
        self.window_debug.layout_plot.addWidget(self.window_debug.plot_widget, 0, 0)
        self.window_debug.plot_item1 = self.window_debug.plot_widget.addPlot()
        self.window_debug.plot_item1.addLegend(size=(150, 80))
        self.window_debug.plot_item1.showGrid(x=True, y=True, alpha=0.5)  #网格透明度
        self.window_debug.btn_config.clicked.connect(self.show_config)
        # self.window_debug.btn_hideMain.clicked.connect(self.hide_main)
        self.window_debug.check_agv_x.toggled.connect(lambda state: self.showGraph(state, 'agv_x'))
        self.window_debug.check_agv_y.toggled.connect(lambda state: self.showGraph(state, 'agv_y'))
        self.window_debug.check_agv_angle.toggled.connect(lambda state: self.showGraph(state, 'agv_angle'))
        self.window_debug.check_agv_battery.toggled.connect(lambda state: self.showGraph(state, 'agv_battery'))
        # self.window_debug.check_agv_speed_tl.toggled.connect(lambda state:self.showGraph(state,'agv_speed_tl'))#t:target调度下发目标 l:line线速度
        # self.window_debug.check_agv_speed_ac.toggled.connect(lambda state:self.showGraph(state,'agv_speed_ac'))#ac:accelerate 加速度
        # self.window_debug.check_agv_location_up.toggled.connect(lambda state:self.showGraph(state,'agv_location_up'))
        self.window_debug.check_imu_angle.toggled.connect(lambda state: self.showGraph(state, 'imu_angle'))
        self.window_debug.check_agv_speed_pl.toggled.connect(lambda state: self.showGraph(state, 'agv_speed_pl'))  #p:plan规划 l:line线速度
        self.window_debug.check_agv_speed_al.toggled.connect(lambda state: self.showGraph(state, 'agv_speed_al'))
        self.window_debug.check_agv_speed_pa.toggled.connect(lambda state: self.showGraph(state, 'agv_speed_pa'))
        self.window_debug.check_agv_speed_aa.toggled.connect(lambda state: self.showGraph(state, 'agv_speed_aa'))  #a:actual实际 a:angle角速度
        self.window_debug.check_motorL_speed_p.toggled.connect(lambda state: self.showGraph(state, 'motorL_speed_p'))
        self.window_debug.check_motorL_speed_a.toggled.connect(lambda state: self.showGraph(state, 'motorL_speed_a'))
        self.window_debug.check_motorR_speed_p.toggled.connect(lambda state: self.showGraph(state, 'motorR_speed_p'))
        self.window_debug.check_motorR_speed_a.toggled.connect(lambda state: self.showGraph(state, 'motorR_speed_a'))
        self.window_debug.check_motorU_speed_p.toggled.connect(lambda state: self.showGraph(state, 'motorU_speed_p'))
        self.window_debug.check_motorU_speed_a.toggled.connect(lambda state: self.showGraph(state, 'motorU_speed_a'))
        self.window_debug.check_motorT_speed_p.toggled.connect(lambda state: self.showGraph(state, 'motorT_speed_p'))
        self.window_debug.check_motorT_speed_a.toggled.connect(lambda state: self.showGraph(state, 'motorT_speed_a'))

        self.window_debug.sig_close.connect(self.close_watch)

        # 显示曲线配置
        self.widget_graphConfig = widget_graphConfig()
        layout = self.widget_graphConfig.gridLayout
        self.group = []
        file_path = parent_dir_path + "/appDoc/config/array_color.txt"
        try:
            with open(file_path, 'r') as file:
                lines = file.readlines()
                for i in range(0, 20):
                    if i < len(lines):
                        self.window_debug.dataColor.append(lines[i].strip())
                    else:
                        self.window_debug.dataColor.append('red')
                        print('hlly')
            # print(self.window_debug.dataColor)
            for i in range(0, 20):
                layout1 = QHBoxLayout()
                group1 = QButtonGroup()
                self.group.append(group1)
                color = ['r', 'g', 'b', 'c', 'm', 'y', 'orange']
                color_name = ['red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'orange']
                for index, item in enumerate(color):
                    radio = QRadioButton(item)
                    radio.setObjectName(str(i))
                    radio.color = color_name[index]
                    radio.clicked.connect(self.on_radioButton_toggled)
                    self.group[i].addButton(radio)
                    layout1.addWidget(radio)
                button = QPushButton()
                button.setObjectName(str(i))
                button.setStyleSheet('QWidget {background-color:%s}' % self.window_debug.dataColor[i])
                button.clicked.connect(self.showColorDialog)
                self.group[i].addButton(button)
                layout1.addWidget(button)
                layout.addLayout(layout1, i, 1)
        except:
            print('曲线颜色配置文件不存在')
        # # 获取布局中的所有widget和子布局中的widget
        # index=0
        # for i in range(layout.count()):
        #     item = layout.itemAt(i)
        #     if item.layout() is not None:
        #         sub_layout = item.layout()
        #         for j in range(sub_layout.count()):
        #             sub_item = sub_layout.itemAt(j)
        #             if sub_item.widget() is not None:
        #                 sub_item.widget().setObjectName(str(index))
        #                 sub_item.widget().clicked.connect(self.on_radioButton_toggled)
        #         index+=1

        #报警指示灯
        self.led_motorLR_1 = Indicator(self.window_debug.indicator_motorLR_1, reverse=True)
        self.led_motorLR_2 = Indicator(self.window_debug.indicator_motorLR_2, reverse=True)
        self.led_motorLR_3 = Indicator(self.window_debug.indicator_motorLR_3, reverse=True)
        self.led_motorLR_4 = Indicator(self.window_debug.indicator_motorLR_4, reverse=True)
        self.led_motorRT_1 = Indicator(self.window_debug.indicator_motorRT_1, reverse=True)
        self.led_motorRT_2 = Indicator(self.window_debug.indicator_motorRT_2, reverse=True)
        self.led_motorRT_3 = Indicator(self.window_debug.indicator_motorRT_3, reverse=True)
        self.led_motorRT_4 = Indicator(self.window_debug.indicator_motorRT_4, reverse=True)
        self.led_motorUP_1 = Indicator(self.window_debug.indicator_motorUP_1, reverse=True)
        self.led_motorUP_2 = Indicator(self.window_debug.indicator_motorUP_2, reverse=True)
        self.led_motorUP_3 = Indicator(self.window_debug.indicator_motorUP_3, reverse=True)
        self.led_motorUP_4 = Indicator(self.window_debug.indicator_motorUP_4, reverse=True)
        self.led_AGV_E1 = Indicator(self.window_debug.indicator_AGV_E1, reverse=True)
        self.led_AGV_E2 = Indicator(self.window_debug.indicator_AGV_E2, reverse=True)
        self.led_AGV_E3 = Indicator(self.window_debug.indicator_AGV_E3, reverse=True)
        self.led_AGV_E4 = Indicator(self.window_debug.indicator_AGV_E4, reverse=True)
        self.led_AGV_W1 = Indicator(self.window_debug.indicator_AGV_W1, reverse=True)
        self.led_AGV_W2 = Indicator(self.window_debug.indicator_AGV_W2, reverse=True)
        self.led_AGV_W3 = Indicator(self.window_debug.indicator_AGV_W3, reverse=True)
        self.led_AGV_W4 = Indicator(self.window_debug.indicator_AGV_W4, reverse=True)

        self.leds = [
            self.led_motorLR_1, self.led_motorLR_2, self.led_motorRT_1, self.led_motorRT_2, self.led_motorRT_3, self.led_motorRT_4,
            self.led_motorUP_1, self.led_motorUP_2, self.led_motorUP_3, self.led_motorUP_4, self.led_AGV_E1, self.led_AGV_E2, self.led_AGV_E3,
            self.led_AGV_E4, self.led_AGV_W1, self.led_AGV_W2, self.led_AGV_W3, self.led_AGV_W4
        ]
        # for i in range(len(self.leds)):
        #     self.leds[i].flash(Indicator.INACTIVE, 300)#闪烁
        # self.led_motorLR_1.active()
        # self.led_motorLR_2.inactive()
        # self.led_motorLR_3.warning()
        # self.led_motorLR_4.flash(Indicator.INACTIVE, 300)#闪烁
        self.led_oldStatus = []
        self.window_debug.input = [
            self.window_debug.input_agv_speed_pl, self.window_debug.input_agv_speed_pa, self.window_debug.input_agv_speed_al,
            self.window_debug.input_agv_speed_aa, self.window_debug.input_agv_x, self.window_debug.input_agv_y, self.window_debug.input_agv_angle,
            self.window_debug.input_agv_speed_tl, self.window_debug.input_agv_speed_ac, self.window_debug.input_motorL_speed_a,
            self.window_debug.input_motorR_speed_a, self.window_debug.input_motorL_speed_p, self.window_debug.input_motorR_speed_p,
            self.window_debug.input_agv_location_up, self.window_debug.input_pgv_tag, self.window_debug.input_pgv_x, self.window_debug.input_pgv_y,
            self.window_debug.input_pgv_angle, self.window_debug.input_imu_angle, self.window_debug.input_motorU_speed_p,
            self.window_debug.input_motorU_speed_a, self.window_debug.input_motorT_speed_p, self.window_debug.input_motorT_speed_a,
            self.window_debug.input_agv_angle_RT, self.window_debug.input_agv_location_RT
        ]
        self.window_debug.label = [
            self.window_debug.label_LRwheel1, self.window_debug.label_LRwheel2, self.window_debug.label_Err1, self.window_debug.label_Err2,
            self.window_debug.label_Err3, self.window_debug.label_Err4, self.window_debug.label_Liftor1, self.window_debug.label_Liftor2,
            self.window_debug.label_Liftor3, self.window_debug.label_Liftor4, self.window_debug.label_Rotator1, self.window_debug.label_Rotator2,
            self.window_debug.label_Rotator3, self.window_debug.label_Rotator4, self.window_debug.label_Warn1, self.window_debug.label_Warn2,
            self.window_debug.label_Warn3, self.window_debug.label_Warn4
        ]
        #endregion
        #region # 加密解密
        # 定义加密模式和填充方式
        self.MODE = AES.MODE_CBC
        self.PADDING = b'\0'
        self.btn_query_uid.clicked.connect(self.fuc_query_uid)
        self.btn_generate_lic.clicked.connect(self.fuc_generate_lic)
        self.btn_authorization.clicked.connect(self.fuc_authorization)
        self.calendarWidget.clicked.connect(self.on_date_clicked)
        #endregion
        #region # 语音播报
        self.flag_voice_download = 0
        self.btn_voice_play.clicked.connect(self.fuc_voice_play)
        self.slider_volume.sliderReleased.connect(self.fuc_voice_setVolume)

        self.btn_voice_addFile.clicked.connect(self.fuc_voice_addFile)
        self.btn_voice_addFiles.clicked.connect(self.fuc_voice_addFiles)
        self.btn_voice_moveUp.clicked.connect(self.fuc_voice_moveUp)
        self.btn_voice_moveDown.clicked.connect(self.fuc_voice_moveDown)
        self.btn_voice_delFile.clicked.connect(self.fuc_voice_delFile)
        self.btn_voice_delFiles.clicked.connect(self.fuc_voice_delFiles)
        self.btn_voice_downloadSel.clicked.connect(self.fuc_voice_downloadSel)
        self.btn_voice_downloadAll.clicked.connect(self.fuc_voice_downloadAll)
        self.btn_voice_update.clicked.connect(self.fuc_voice_update)
        self.btn_voice_exit.clicked.connect(self.fuc_voice_exit)

        #endregion
        #region # 输入输出
        self.led_inputors = []
        i = 0
        vLayout = QVBoxLayout()
        for i in range(4):
            hLayout = QHBoxLayout()
            for j in range(7):
                button_test = QPushButton()
                self.led_inputor = Inputor(button_test)
                self.led_inputors.append(self.led_inputor)
                label = QLabel(f'输入 {i*7 + j + 1}')
                hLayout.addWidget(button_test)
                hLayout.addWidget(label)
            vLayout.addLayout(hLayout)
        self.groupBox_7.setLayout(vLayout)

        self.check_out_1.toggled.connect(self.fuc_outputControl)
        self.check_out_2.toggled.connect(self.fuc_outputControl)
        self.check_out_3.toggled.connect(self.fuc_outputControl)
        self.check_out_4.toggled.connect(self.fuc_outputControl)
        self.check_out_5.toggled.connect(self.fuc_outputControl)
        self.check_out_6.toggled.connect(self.fuc_outputControl)
        self.check_out_7.toggled.connect(self.fuc_outputControl)
        self.check_out_8.toggled.connect(self.fuc_outputControl)
        self.check_out_9.toggled.connect(self.fuc_outputControl)
        self.check_out_10.toggled.connect(self.fuc_outputControl)
        self.check_out_11.toggled.connect(self.fuc_outputControl)
        self.check_out_12.toggled.connect(self.fuc_outputControl)
        self.check_out_13.toggled.connect(self.fuc_outputControl)
        self.check_out_14.toggled.connect(self.fuc_outputControl)
        self.check_out_15.toggled.connect(self.fuc_outputControl)
        self.check_out_16.toggled.connect(self.fuc_outputControl)
        self.check_out_17.toggled.connect(self.fuc_outputControl)
        self.check_out_18.toggled.connect(self.fuc_outputControl)
        self.check_out_19.toggled.connect(self.fuc_outputControl)
        self.check_out_20.toggled.connect(self.fuc_outputControl)
        self.check_out_21.toggled.connect(self.fuc_outputControl)
        self.check_out_22.toggled.connect(self.fuc_outputControl)
        self.check_out_23.toggled.connect(self.fuc_outputControl)
        self.check_out_24.toggled.connect(self.fuc_outputControl)
        #endregion
        #region # 调度协议转换
        self.window_tool_convertMsg = Window_tool_convertMsg()
        self.action_tool_convertMeg.triggered.connect(self.open_tool_convertMsg)
        #endregion
        #region # 清洗机驱动板测试
        self.slider_driver_pwm.sliderReleased.connect(self.fuc_driver_pwm)
        self.slider_driver_speed.sliderReleased.connect(self.fuc_driver_speed)
        #endregion
        #region # 程序监控
        self.btn_query_softwareVersion.clicked.connect(self.fuc_query_softwareVersion)
        self.checkBox_print_threadName.stateChanged.connect(self.fuc_print_threadName)
        #endregion

    #region # 定义按键监听回调函数
    def on_press(self, key):
        if self.AppIsFocus == True and self.stackedWidget.currentIndex() == 0 and self.tab_widget.currentIndex() == 0:
            if self.keyIsPressed == False:
                self.keyIsPressed = True
                # print("键盘按下")
                try:
                    if key.char == 'w':
                        self.fuc_agv_moveForward()
                    elif key.char == 's':
                        self.fuc_agv_moveBack()
                    elif key.char == 'a':
                        self.fuc_agv_rotateCCW()
                    elif key.char == 'd':
                        self.fuc_agv_rotateCW()

                except AttributeError:
                    if key == keyboard.Key.space:
                        self.fuc_agv_stop()

    def on_release(self, key):
        if self.AppIsFocus == True and self.stackedWidget.currentIndex() == 0 and self.tab_widget.currentIndex() == 0:
            self.keyIsPressed = False
            # print('按键松开')
    def focusInEvent(self, event):  # 聚焦时开启键盘监听，快捷键控制AGV运动
        self.AppIsFocus = True
        # print("聚焦")
    def focusOutEvent(self, event):  # 失焦时关闭键盘监听,防止快捷键误触发AGV运动
        self.AppIsFocus = False
        # print("失焦")

    #endregion
    #region # 参数配置_上位机
    def change_protocol(self, index):
        self.protocol = self.sender().currentText()
        if self.protocol == 'My':
            sendData1 = [0xAA, 0xAA, 0xAA, 0xAA, 0xAA]
        elif self.protocol == 'TCS':
            sendData1 = [0xBB, 0xBB, 0xBB, 0xBB, 0xBB]
        # print(widget_connect.com_protocol)
        # if (self.mode_debug=="客户端" and self.widget_connect.com_protocol.currentText()=='My')\
        #     or (self.mode_debug=="串口" and self.com_protocol_1.currentText()=='My'):
        #     sendData1=[0xAA,0xAA,0xAA,0xAA,0xAA]
        # elif (self.mode_debug=="客户端" and self.widget_connect.com_protocol.currentText()=='TCS')\
        #     or (self.mode_debug=="串口" and self.com_protocol_1.currentText()=='TCS'):
        #     sendData1=[0xBB,0xBB,0xBB,0xBB,0xBB]
        # if self.widget_connect.com_protocol.currentText()=='My':
        #     sendData1=[0xAA,0xAA,0xAA,0xAA,0xAA]
        # elif self.widget_connect.com_protocol.currentText()=='TCS':
        #     sendData1=[0xBB,0xBB,0xBB,0xBB,0xBB]

        sendData2 = bytes(sendData1)
        if self.widget_connect.check_mode_wifiToUart.isChecked():
            global_s.send(sendData2)
        elif self.rxflag:
            self.ser.write(sendData2)
        elif self.flag_server:
            self.server_thread.signal_send_data.emit(sendData2)
        if (self.widget_log.check_showsSendMessage.isChecked()):  #日志打印发送数据
            self.fuc_log("Tx:" + "  " + HexStrAddSpace(sendData2.hex()))

    def change_debugMode(self, debugMode):
        # print(debugMode)
        # print("hlly")
        debugMode = self.sender().text()
        self.mode_debug = debugMode
        if debugMode == "串口":
            self.show_all_widgets(self.widget_connect.layout_deviceState)
            self.show_all_widgets(self.widget_connect.gridLayout_com)
            self.hide_all_widgets(self.widget_connect.gridLayout_client)
            self.hide_all_widgets(self.widget_connect.gridLayout_server)
        elif debugMode == "客户端":
            self.show_all_widgets(self.widget_connect.layout_deviceState)
            self.show_all_widgets(self.widget_connect.gridLayout_client)
            self.hide_all_widgets(self.widget_connect.gridLayout_server)
            self.hide_all_widgets(self.widget_connect.gridLayout_com)
        elif debugMode == "服务端":
            # self.widget_connect.btn_server.setVisible(True)
            # self.btn_client.setVisible(False)
            self.show_all_widgets(self.widget_connect.gridLayout_server)
            self.hide_all_widgets(self.widget_connect.gridLayout_client)
            self.hide_all_widgets(self.widget_connect.gridLayout_com)
            self.hide_all_widgets(self.widget_connect.layout_deviceState)

    def get_index_fromTabname(self, tab_name='1'):  #根据标签页名称获取标签页ID
        for index in range(self.tab_widget.count()):
            tabname = self.tab_widget.tabText(index)
            if tabname == tab_name:
                return index
        return 0

    def show_widget_config(self):
        # index = self.tab_widget.indexOf(self.tab_config)
        # if index == -1:
        self.tab_widget.addTab(self.tab_config, '启动参数配置')
        p_tabwidget_id = self.get_index_fromTabname('启动参数配置')
        self.tab_widget.setCurrentIndex(p_tabwidget_id)  #显示标签页

    def fuc_config_save(self):  #另存为配置
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/config")
        with open(filename, "w", encoding='utf-8') as f:
            found_widget = self.widget_config.findChildren(eval('QComboBox'))  #找出所有QComboBox对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + found_widget[i].currentText() + "\n")
            found_widget = self.widget_config.findChildren(eval('QLineEdit'))  #找出所有QLineEdit对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + found_widget[i].text() + "\n")
            found_widget = self.widget_config.findChildren(eval('QCheckBox'))  #找出所有QLineEdit对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + ('True' if found_widget[i].isChecked() else 'False') + "\n")
            f.close()
        self.widget_config.info.setText(get_time() + "  " + "配置文件已保存至" + filename)

    def fuc_config_set(self):  #保存到默认配置
        filename = parent_dir_path + "/appDoc/config/default.txt"  #启动加载时如果检测到文件夹不存在已经创建了文件夹
        with open(filename, "w", encoding='utf-8') as f:
            found_widget = self.widget_config.findChildren(eval('QComboBox'))  #找出所有QComboBox对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + found_widget[i].currentText() + "\n")
            found_widget = self.widget_config.findChildren(eval('QLineEdit'))  #找出所有QLineEdit对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + found_widget[i].text() + "\n")
            found_widget = self.widget_config.findChildren(eval('QCheckBox'))  #找出所有QLineEdit对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + ('True' if found_widget[i].isChecked() else 'False') + "\n")
            f.close()
        self.widget_config.info.setText(get_time() + "  " + "配置文件已保存至" + filename)

    def fuc_config_load(self):  #加载配置
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/config")
        if filename:
            with open(filename, 'r', encoding='utf-8') as f:
                lines = f.readlines()
                for line in lines:
                    if '#' in line:
                        line = line[:line.index('#')]  #去除每行#后的内容
                    if line.strip():  #去除字符串两端的空白字符（包括空格、换行符和制表符）
                        parameter = line.strip().split(":", 1)
                        search_string = parameter[0]
                        found_widget = self.widget_config.findChild(QLineEdit, search_string)  #根据字符串查找widget中的对象
                        if found_widget:
                            found_widget.setText(parameter[1])
                        else:
                            found_widget = self.widget_config.findChild(QComboBox, search_string)  #根据字符串查找widget中的对象
                            if found_widget:
                                found_widget.setCurrentText(parameter[1])  #必须是下拉列表中的已有项
                            else:
                                found_widget = self.widget_config.findChild(QCheckBox, search_string)
                                found_widget.setChecked(True if parameter[1] == 'True' else False)

            self.fuc_log('加载完成')
        else:
            self.fuc_log('取消加载')

    #endregion
    #region # AGV内部参数监测界面
    def show_main(self):
        # self.layout_route.addWidget(self.paint)
        self.show()

    def hide_main(self):
        self.window_debug.gridLayout.addWidget(self.paint)
        self.hide()

    def showGraph(self, state, item):
        if state:  # 添加曲线
            if item == 'agv_x':
                self.window_debug.curve_agv_x = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[0], name=item)  #symbol='o'
            elif item == 'agv_y':
                self.window_debug.curve_agv_y = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[1], name=item)
            elif item == 'agv_angle':
                self.window_debug.curve_agv_angle = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[2], name=item)
            elif item == 'agv_battery':
                self.window_debug.curve_agv_battery = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[3], name=item)
            elif item == 'agv_speed_tl':
                self.window_debug.curve_agv_speed_tl = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[4], name=item)
            elif item == 'agv_speed_ac':
                self.window_debug.curve_agv_speed_ac = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[5], name=item)
            elif item == 'agv_location_up':
                self.window_debug.curve_agv_location_up = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[6], name=item)
            elif item == 'imu_angle':
                self.window_debug.curve_imu_angle = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[7], name=item)
            elif item == 'agv_speed_pl':
                self.window_debug.curve_agv_speed_pl = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[8], name=item)
            elif item == 'agv_speed_al':
                self.window_debug.curve_agv_speed_al = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[9], name=item)
            elif item == 'agv_speed_pa':
                self.window_debug.curve_agv_speed_pa = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[10], name=item)
            elif item == 'agv_speed_aa':
                self.window_debug.curve_agv_speed_aa = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[11], name=item)
            elif item == 'motorL_speed_p':
                self.window_debug.curve_motorL_speed_p = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[12], name=item)
            elif item == 'motorL_speed_a':
                self.window_debug.curve_motorL_speed_a = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[13], name=item)
            elif item == 'motorR_speed_p':
                self.window_debug.curve_motorR_speed_p = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[14], name=item)
            elif item == 'motorR_speed_a':
                self.window_debug.curve_motorR_speed_a = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[15], name=item)
            elif item == 'motorU_speed_p':
                self.window_debug.curve_motorU_speed_p = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[16], name=item)
            elif item == 'motorU_speed_a':
                self.window_debug.curve_motorU_speed_a = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[17], name=item)
            elif item == 'motorT_speed_p':
                self.window_debug.curve_motorT_speed_p = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[18], name=item)
            elif item == 'motorT_speed_a':
                self.window_debug.curve_motorT_speed_a = self.window_debug.plot_item1.plot(pen=self.window_debug.dataColor[19], name=item)

        else:  # 移除曲线
            if item == 'agv_x': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_x)
            elif item == 'agv_y': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_y)
            elif item == 'agv_speed_pl': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_pl)
            elif item == 'agv_speed_al': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_al)
            elif item == 'agv_speed_pa': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_pa)
            elif item == 'agv_speed_aa': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_aa)
            elif item == 'motorL_speed_p': self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorL_speed_p)
            elif item == 'motorL_speed_a': self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorL_speed_a)
            elif item == 'motorR_speed_p': self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorR_speed_p)
            elif item == 'motorR_speed_a': self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorR_speed_a)
            elif item == 'motorU_speed_p': self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorU_speed_p)
            elif item == 'motorU_speed_a': self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorU_speed_a)
            elif item == 'motorT_speed_p': self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorT_speed_p)
            elif item == 'motorT_speed_a': self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorT_speed_a)
            elif item == 'agv_speed_tl': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_tl)
            elif item == 'agv_speed_ac': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_ac)
            elif item == 'agv_location_up': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_location_up)
            elif item == 'imu_angle': self.window_debug.plot_item1.removeItem(self.window_debug.curve_imu_angle)
            elif item == 'agv_angle': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_angle)
            elif item == 'agv_battery': self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_battery)

    def show_config(self):
        self.widget_graphConfig.show()

    def showColorDialog(self):
        sender = self.sender()
        col = QColorDialog.getColor()
        print(col.name(), "\n")
        self.window_debug.dataColor[int(sender.objectName())] = col.name()
        if col.isValid():
            print('hlly')
            sender.setStyleSheet('QWidget {background-color:%s}' % col.name())
            file_path = parent_dir_path + "/appDoc/config/array_color.txt"
            with open(file_path, 'w') as f:
                for item in self.window_debug.dataColor:
                    f.write("%s\n" % item)

    def on_radioButton_toggled(self, index):
        sender = self.sender()
        self.window_debug.dataColor[int(sender.objectName())] = sender.color
        file_path = parent_dir_path + "/appDoc/config/array_color.txt"
        with open(file_path, 'w') as f:
            for item in self.window_debug.dataColor:
                f.write("%s\n" % item)

        parent_widget = self.sender.widget()
        print(parent_widget)
        # # 检查并打印父对象
        # if parent_widget:
        #     print("Button's parent:", parent_widget)
        # else:
        #     print("Button has no parent.")
    def close_watch(self):  #关闭监测界面时触发
        self.action_watch.setChecked(False)

    #endregion
    #region # 界面显示/隐藏控制
    def add_tab(self, title):
        tab = QWidget()
        layout = QVBoxLayout()

        label = QLabel(title)
        layout.addWidget(label)

        button = QPushButton("关闭")
        button.clicked.connect(lambda: self.close_tab(tab))
        layout.addWidget(button)

        tab.setLayout(layout)
        self.tab_widget.addTab(tab, title)

    def changeTabName(self, index):  # 双击设置选项卡名称
        new_name, ok = QInputDialog.getText(self.tab_widget, "修改名称", "输入新名称:")
        if ok and new_name:
            self.tab_widget.setTabText(index, new_name)

    def deltab(self, index):  # 删除选项卡
        print(self.tab_widget.currentIndex())
        print(self.tab_widget.tabText(index))

    def on_tab_changed(self, index):
        tabname = self.tab_widget.tabText(index)
        if tabname == "CTU":
            self.widget_ctu_up.layout_connect.addWidget(self.widget_connect)
            self.widget_ctu_up.layout_log.addWidget(self.widget_log)
        elif tabname == "清洗机":
            self.widget_clean.layout_connect.addWidget(self.widget_connect)
            self.widget_clean.layout_log.addWidget(self.widget_log)
        elif tabname == "LED":
            self.widget_leds.layout_connect.addWidget(self.widget_connect)
            self.widget_leds.layout_log.addWidget(self.widget_log)
        else:
            self.layout_log.addWidget(self.widget_log)
            self.layout_connect.addWidget(self.widget_connect)

    def fuc_change_stack(self):
        current_index = self.bar_left.currentRow()
        tabname = self.bar_left.currentItem().text()
        if tabname == "二维码标定":
            self.tab_widget.addTab(self.widget_tags, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "陀螺仪数据":
            self.tab_widget.addTab(self.widget_imu, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "清洗机":
            self.widget_clean.layout_connect.addWidget(self.widget_connect)
            self.widget_clean.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_clean, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "CTU":
            self.widget_ctu_up.layout_connect.addWidget(self.widget_connect)
            self.widget_ctu_up.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_ctu_up, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "LED":
            self.widget_leds.layout_connect.addWidget(self.widget_connect)
            self.widget_leds.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_leds, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "清洗机驱动板":
            self.stackedWidget.setCurrentIndex(13)
            p_tabwidget_id = self.get_index_fromTabname("设备控制")
        elif tabname == "路径跟踪":
            self.stackedWidget.setCurrentIndex(14)
            p_tabwidget_id = self.get_index_fromTabname("设备控制")
        else:
            self.layout_log.addWidget(self.widget_log)
            self.layout_connect.addWidget(self.widget_connect)
            self.stackedWidget.setCurrentIndex(current_index)  # 将StackedWidget的当前索引与ListWidget的当前选中项关联起来
            p_tabwidget_id = self.get_index_fromTabname("设备控制")
        self.tab_widget.setCurrentIndex(p_tabwidget_id)  #显示标签页

    def close_tab(self, index):  # 关闭选项卡
        # print(tab)

        tabname = self.tab_widget.tabText(index)
        if tabname != "设备控制":
            self.tab_widget.removeTab(index)
        # index = self.tab_widget.indexOf(tab)
        # if index != -1:
        #     self.tab_widget.removeTab(index)

    def hide_all_widgets(self, layout):
        for i in range(layout.count()):
            widget = layout.itemAt(i).widget()
            if widget is not None:
                widget.setVisible(False)

    def show_all_widgets(self, layout):
        for i in range(layout.count()):
            widget = layout.itemAt(i).widget()
            if widget is not None:
                widget.setVisible(True)

    def toggle_area_log(self, checked):
        if checked:
            self.show_all_widgets(self.area_log)
            self.show_all_widgets(self.area_log_control)
            self.show_all_widgets(self.widget_log.area_log)
            self.show_all_widgets(self.widget_log.area_log_control)
        else:
            self.hide_all_widgets(self.area_log)
            self.hide_all_widgets(self.area_log_control)
            self.hide_all_widgets(self.widget_log.area_log)
            self.hide_all_widgets(self.widget_log.area_log_control)
            self.timer13.start(50)

    def toggle_area_map(self, checked):
        if checked:
            self.show_all_widgets(self.layout_route)
            self.show_all_widgets(self.layout_route_control)
        else:
            self.hide_all_widgets(self.layout_route)
            self.hide_all_widgets(self.layout_route_control)
            # self.resize(1300, 1000)
            self.timer13.start(50)  #直接立即修改窗口大小无效，改为通过定时器延时50ms后修改

    def toggle_area_video(self, checked):
        if checked:
            # self.cap = cv2.VideoCapture(1)
            self.show_all_widgets(self.area_video)
            self.show_all_widgets(self.horizontalLayout_3)
            self.video_thread.start()
            self.flag_camera = 1
        else:
            if self.flag_camera:
                self.flag_camera = 0
                self.video_thread.stop()
                print("摄像头已关闭")
            self.hide_all_widgets(self.area_video)  #隐藏窗口
            self.hide_all_widgets(self.horizontalLayout_3)
            self.timer13.start(50)  #重置窗口大小
            # self.resize(1300, 1000)

    def toggle_window_debug(self, checked):
        if checked:
            self.window_debug.gridLayout.addWidget(self.paint)  #添加运动轨迹绘制窗口
            self.paint.setMinimumWidth(500)  #设置widget最小宽度
            self.window_debug.show()
        else:
            self.layout_route.addWidget(self.paint)
            self.window_debug.hide()

    def open_tool_convertMsg(self):
        self.window_tool_convertMsg.show()
        print("hlly")

    #endregion
    #region # 打开/关闭 串口
    def fuc_OpenCom(self):
        if (self.widget_connect.btn_com.text() == '打开串口') and self.portList.currentText():
            portFullname = self.portList.currentText()  # 设置端口
            self.ser.port = portFullname[0:portFullname.find(" ")]
            self.ser.baudrate = int(self.widget_connect.com_comBaud.currentText())  # 波特率
            self.ser.bytesize = 8  # 数据位
            self.ser.parity = 'N'  # 校验位
            self.ser.stopbits = 1  # 停止位
            if self.ser.baudrate == 9600:
                self.ser.timeout = 0.018  # 超时时间（9600）   接收雷达0.016-0.018
            elif self.ser.baudrate == 115200:
                self.ser.timeout = 0.007  # 超时时间（115200）

            # self.ser.inter_byte_timeout = 0.001                              # 超时时间（9600）   接收雷达0.016-0.018
            # self.ser.set_buffer_size(rx_size=12800, tx_size=12800)

            try:
                self.ser.open()
                # 打开线程接收
                thread_com = threading.Thread(target=self.Serial)
                self.rxflag = 1
                thread_com.start()

            except serial.SerialException:
                QMessageBox.information(None, 'Open Port Error', '此串口不能正常打开！')
                return None
            if self.ser.isOpen():
                self.widget_connect.btn_com.setText('关闭串口')
                # self.widget_connect.btn_com.setStyleSheet(
                #     QPushButton{background:#9AFF9A;border-radius:5px;})

        elif self.widget_connect.btn_com.text() == '关闭串口':
            try:
                self.rxflag = 0
                self.ser.close()
            except:
                QMessageBox.critical(None, 'Open Port Error', '此串口不能正常关闭！')
                return None
            self.widget_connect.btn_com.setText('打开串口')
            # self.widget_connect.btn_com.setStyleSheet(
            #     QPushButton{background:orange;border-radius:5px;})
    def Serial(self):
        while self.rxflag:
            try:
                if self.ser.in_waiting:
                    data = self.ser.readall()  #不能接收持续发送的数据
                    if not data or data == 'exit':
                        print("break")
                        break
                    self.signal.data_process.emit(data)
            except Exception:
                print("wrong")
                # exit()
                self.signal.print.emit("串口异常关闭")
                self.fuc_OpenCom()

    #endregion
    #region # 服务器控制（作为服务器）
    def start_server(self):  #启动服务器
        if self.flag_server == False:
            self.widget_connect.btn_server.setText("关闭服务器")
            self.flag_server = True
            self.server_thread.start()
        else:
            self.widget_connect.btn_server.setText("开启服务器")
            self.flag_server = False
            self.server_thread.stop()
            self.widget_connect.com_clients.clear()  #清空客户端列表

    def show_received_data(self, client_address, data):
        self.fuc_log(f"收到来自 {client_address} 的数据： {data}")

    def update_clients_list(self, flag, client_info):
        item_text = client_info[0] + ':' + str(client_info[1])
        if (self.widget_connect.com_clients.findText(item_text) == -1) & (flag == "add"):  #没有找到
            self.widget_connect.com_clients.insertItem(0, item_text)  #添加新项，并将新项的索引设为0
            self.widget_connect.com_clients.setCurrentIndex(0)  #将下拉框的当前索引设置为添加的项的位置
        elif (self.widget_connect.com_clients.findText(item_text) != -1) & (flag == "remove"):
            self.widget_connect.com_clients.removeItem(self.widget_connect.com_clients.findText(item_text))

    def controlDevice_change(self, num):  #切换当前控制设备
        self.controlDevice = self.widget_connect.com_clients.currentText()
        self.server_thread.sig_controlDevice_change.emit(self.controlDevice)

    #endregion
    #region # 连接/断开 服务器 (作为客户端)
    def connect_server(self):
        global global_flag_client, global_s
        if (global_flag_client == 0):
            global_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            try:
                global_s.settimeout(0.3)  #设置socket超时时间后，没有接收到数据连接也会断开
                s_ip = self.widget_connect.com_server.currentText().split(":")[0]
                s_port = int(self.widget_connect.comboBox_serverPort.currentText())
                global_s.connect((s_ip, s_port))
                global_s.settimeout(None)  #连接成功后重新设置

                self.fuc_log("服务器已连接")
                global_flag_client = 1
                print(global_flag_client)
                # self.widget_connect.label_device_state.setText("在线")
                thread_client = threading.Thread(target=self.server_process)
                thread_client.start()
            except socket.error as e:
                # print("连接错误:", e)
                self.fuc_log("连接失败")
                self.widget_connect.check_mode_wifiToUart.setChecked(False)
                if e.errno == 111:  # Errno 111 对应 "连接被拒绝"
                    print("服务端已关闭")
            except socket.timeout:
                self.signal.show_msg.emit('连接超时！')
                # global_s.close()
            except:
                self.signal.show_msg.emit('连接失败')
                # global_s.close()
        else:

            global_flag_client = 0
            global_s.close()
            self.fuc_log("服务器已断开")

    def server_process(self):
        global global_flag_client, global_s
        while global_flag_client:
            try:
                data = global_s.recv(1024)
            except:
                break
            if not data: break
            if self.widget_connect.check_mode_wifiToUart.isChecked():
                # self.fuc_data_process(data)
                # print(len(data))
                self.signal.data_process.emit(data)
            else:
                self.server_parse_data(data)

    def fuc_com_server_change(self):
        if self.widget_connect.com_server.currentText() == 'host':
            self.widget_connect.com_server.setCurrentText("127.0.0.1")

    def fuc_check_mode_wifiToUart(self):
        global global_flag_client
        if self.widget_connect.check_mode_wifiToUart.isChecked(): global_flag_client = 0
        else:
            global_flag_client = 1
            # self.widget_connect.label_device_state.setText("离线")
        self.connect_server()

    #endregion
    #region # 连接多个服务器 (作为客户端)
    def toggle_widget_servers(self, checked):
        self.widget_servers.show()

    def servers_handle(self, identify, id, data):  #接收数据处理
        self.servers_data_restore(identify, id, data)

    def servers_data_restore(self, identify, id, data):  # 接收数据还原（去转义）
        pattern = rb'(?<!\\);'  # 如果';'前为'\'则不进行分割
        datalist = [x for x in re.split(pattern, data) if x]  #去掉空''
        for i in datalist:
            pattern = rb'[\\](?=;)'
            i = re.sub(pattern, b"", i)  #如果'\'后面是';'去掉'\'
            if len(i) > 1:
                # print(len(data))
                self.servers_data_classify(identify, id, i)
            else:
                print(data)

    def servers_data_classify(self, identify, id, data):  # 接收数据分类
        if (data[0] == 0x50):  #接收十六进制数组
            a = HexStrAddSpace(data.hex())
            self.fuc_log(str(identify) + a)
        elif ((data[0] == 0x42) & (data[1] == 0x44)):  #BD（0x42,0x44）来自于控制板的数据
            self.servers_data_verify(identify, id, data)
        else:  #接收中文或英文或其它
            if self.widget_log.check_showHex.isChecked():
                a = HexStrAddSpace(data.hex())
            else:
                try:
                    a = data.decode('utf-8')
                    print("utf-8")
                except Exception:
                    try:
                        a = data.decode('gbk')
                        print("gbk")
                    except:
                        a = str(data)
                        print("字符串")
            self.fuc_log(str(identify) + a)
        # except:
        #     self.fuc_log("接收数据分类错误")
    def servers_data_verify(self, identify, id, data):  # 接收数据验证
        # print(len(data))
        # if(len(data)>3):
        try:
            if ((len(data) > 8) & (len(data) == data[3]) & (data[len(data) - 1] == 0xda)):  #帧头、帧尾、长度都对
                # print("单帧")
                self.servers_data_apply(identify, id, data[4:len(data) - 2])
                # a=HexStrAddSpace(data.hex())#查看原始数据
                # self.fuc_log(str(identify)+a)
            elif ((len(data) < 300) & (len(data) > data[3])):  #多帧数据拆分开处理
                processData = data[:data[3]]
                # self.fuc_data_verify(processData) #!!!数据合格分开后，重新验证
                self.servers_data_verify(identify, id, processData)  #!!!数据合格分开后，重新验证，不能直接调用fuc_data_apply！！！
                remainData = data[data[3]:]
                # print("多帧")
                # print("总长度"+str(len(data))+'长度'+str(data[3]))
                self.servers_data_classify(identify, id, remainData)
                # print("剩余长度"+str(len(remainData)))
            else:
                if (data[len(data) - 1] != 0xda):
                    # self.fuc_log(str(identify)+"帧尾错误")
                    try:
                        print(data)
                        a = data.decode('utf-8')
                        print("english")
                    except Exception:
                        try:
                            # a=data.decode('gbk')
                            a = HexStrAddSpace(data.hex())
                            print("中文")
                        except:
                            a = str(data)
                    # self.fuc_log(str(identify)+HexStrAddSpace(data.hex()))
                    self.fuc_log(str(identify) + a)
                else:
                    print("数据格式错误")
                    # self.fuc_data_apply(data[4:len(data)-2])
        except:
            a = HexStrAddSpace(data.hex())
            self.fuc_log(str(identify) + a)

    def servers_data_apply(self, identify, id, data):  # 接收数据应用
        if ((data[0] == 0x00) & (data[1] == 0x00)):  #升级进度
            data = data[2:]
            if data[0] == 0xff:
                self.fuc_log(str(identify) + "升级失败")
            else:
                lenth = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
                try:
                    self.servers_pgBars[id].setValue(lenth)
                except Exception:
                    self.fuc_log(str(identify) + "返回数据错误")
                if self.fileLen == lenth:
                    self.fuc_log(str(identify) + "升级完成")

    def servers_chooseAll(self):
        if self.widget_servers.check_all.isChecked():
            for i in range(len(self.servers_checkBoxs)):
                self.servers_checkBoxs[i].setChecked(True)
        else:
            for i in range(len(self.servers_checkBoxs)):
                self.servers_checkBoxs[i].setChecked(False)

    def servers_disConnect(self, id, socketThread):
        print('hlly_disconnect', socketThread)
        self.servers_thread.remove(socketThread)
        print(self.servers_thread)
        self.servers_labels[id].setText('已断开')
        self.servers_labels[id].setStyleSheet("color: red;")

    def servers_disConnectAll(self):
        for i in range(len(self.servers_thread)):
            self.servers_thread[i].stop()
        self.servers_thread = []
        for i in range(len(self.servers_labels)):
            self.servers_labels[i].setText('未连接')
            self.servers_labels[i].setStyleSheet("color: black;")

    def servers_connect(self, i):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(0.3)
        s_ip = self.servers_inputs[2 * i].text()
        s_port = int(self.servers_inputs[2 * i + 1].text())
        try:
            s.connect((s_ip, s_port))
            s.settimeout(None)
            self.fuc_log(str(s.getpeername()) + "连接成功")
            self.servers_labels[i].setText('已连接')
            self.servers_labels[i].setStyleSheet("color: green;")
            socketThread = clientThread(i, s)
            socketThread.log.connect(self.fuc_log)
            socketThread.data_received.connect(self.servers_handle)
            socketThread.disConnect.connect(self.servers_disConnect)
            socketThread.start()
            self.servers_thread.append(socketThread)
            print(self.servers_thread)

        except socket.error as e:
            self.fuc_log(str(s.getpeername()) + str(e) + "连接失败")
            self.servers_labels[i].setText('未连接')
            self.servers_labels[i].setStyleSheet("color: red;")

    def servers_connectAll(self):
        # # 假设你要连接多个服务器
        # servers = [('10.0.0.156', 5001), ('10.0.0.156', 5002)]
        # # servers = [('10.0.0.156', 5001)]

        # for server in servers:
        #     worker = SocketWorker(server)
        #     worker.data_received.connect(self.servers_handle)
        #     worker.log.connect(self.fuc_log)
        #     worker.start()
        #     self.servers_thread.append(worker)
        # print(self.servers_thread)

        for i in range(len(self.servers_labels)):
            if self.servers_checkBoxs[i].isChecked():
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(0.3)  #设置socket超时时间后，没有接收到数据连接也会断开
                s_ip = self.servers_inputs[2 * i].text()
                s_port = int(self.servers_inputs[2 * i + 1].text())
                try:
                    s.connect((s_ip, s_port))
                    s.settimeout(None)
                    self.fuc_log(str(s.getpeername()) + "连接成功")
                    self.servers_labels[i].setText('已连接')
                    self.servers_labels[i].setStyleSheet("color: green;")
                    socketThread = clientThread(i, s)
                    socketThread.log.connect(self.fuc_log)
                    socketThread.data_received.connect(self.servers_handle)
                    socketThread.disConnect.connect(self.servers_disConnect)
                    socketThread.start()
                    self.servers_thread.append(socketThread)
                except socket.error as e:
                    self.fuc_log(str(s.getpeername()) + str(e) + "连接失败")
                    self.servers_labels[i].setText('未连接')
                    self.servers_labels[i].setStyleSheet("color: red;")

    def servers_updateProgress(self, i):
        print(self.servers_sliders[i].value())
        print(i)

    #endregion
    #region # 日志管理
    def fuc_log(self, text="", type="", save=False):  #日志显示区打印
        self.signal.print.emit(text)

    def sigCall_print(self, text=""):
        log = get_time() + "  " + text
        self.widget_log.textBrowser.append(log)
        self.fuc_savelog(log)

    def fuc_openlog(self):  #打开日志
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/log")
        if filename: os.startfile(filename)

    def fuc_savelog(self, text):  #保存日志
        if self.widget_log.check_record.isChecked():
            filepath = "appDoc/log/{0}.txt".format(self.widget_log.input_record_name.text())
            with open(filepath, "a+", encoding='utf-8') as f:
                f.write("{}\n".format(text))  # 自带文件关闭功能，不需要再写f.close()

    def fuc_clear(self, text=""):  #日志显示区清空
        self.widget_log.textBrowser.clear()
        # self.fuc_log("hlly\r\n")

    #endregion
    #region # 界面数据更新(进程处理完回调)
    def sigCall_show_msg(self, text=""):
        QMessageBox.about(self, "note", text)

    def sigCall_update_deviceStatus(self, text=""):  #更新设备在线状态
        self.widget_connect.label_device_state.setText(text)

    def sigCall_update_angle(self, x, y):
        self.angleCurve.plot.setData(x, y)

    def sigCall_update_imuRawData(self, x, imuRawData, average):
        for i in range(6):  #更新6条曲线数据
            self.widget_imu.curve[i].setData(x, imuRawData[i])
            self.widget_imu.average[i].setText(str(average[i]))
        gyroX = float(self.widget_imu.input_gyroX.text())
        gyroY = float(self.widget_imu.input_gyroY.text())
        gyroZ = float(self.widget_imu.input_gyroZ.text())
        sumGyro = math.sqrt((average[0] - gyroX)**2 + (average[1] - gyroY)**2 + (average[2] - gyroZ)**2)  #对应xyz轴
        self.widget_imu.input_average_gyro.setText(str(sumGyro))
        # print(average)
    def sigCall_update_debugGraph(self, x, graphData):  #监视AGV参数及绘制曲线
        if self.window_debug.check_agv_speed_pl.isChecked(): self.window_debug.curve_agv_speed_pl.setData(x, graphData[0])
        if self.window_debug.check_agv_speed_pa.isChecked(): self.window_debug.curve_agv_speed_pa.setData(x, graphData[1])
        if self.window_debug.check_agv_speed_al.isChecked(): self.window_debug.curve_agv_speed_al.setData(x, graphData[2])
        if self.window_debug.check_agv_speed_aa.isChecked(): self.window_debug.curve_agv_speed_aa.setData(x, graphData[3])
        if self.window_debug.check_agv_x.isChecked(): self.window_debug.curve_agv_x.setData(x, graphData[4])
        if self.window_debug.check_agv_y.isChecked(): self.window_debug.curve_agv_y.setData(x, graphData[5])
        if self.window_debug.check_agv_angle.isChecked(): self.window_debug.curve_agv_angle.setData(x, graphData[6])
        # if self.window_debug.check_agv_speed_tl.isChecked():self.window_debug.curve_agv_speed_tl.setData(x, graphData[7])
        # if self.window_debug.check_agv_speed_ac.isChecked():self.window_debug.curve_agv_speed_ac.setData(x, graphData[8])
        if self.window_debug.check_motorL_speed_a.isChecked(): self.window_debug.curve_motorL_speed_a.setData(x, graphData[9])
        if self.window_debug.check_motorR_speed_a.isChecked(): self.window_debug.curve_motorR_speed_a.setData(x, graphData[10])
        if self.window_debug.check_motorL_speed_p.isChecked(): self.window_debug.curve_motorL_speed_p.setData(x, graphData[11])
        if self.window_debug.check_motorR_speed_p.isChecked(): self.window_debug.curve_motorR_speed_p.setData(x, graphData[12])
        # if self.window_debug.check_agv_location_up.isChecked():self.window_debug.curve_agv_location_up.setData(x, graphData[13])
        if self.window_debug.check_imu_angle.isChecked(): self.window_debug.curve_imu_angle.setData(x, graphData[18])
        if self.window_debug.check_motorU_speed_p.isChecked(): self.window_debug.curve_motorU_speed_p.setData(x, graphData[19])
        if self.window_debug.check_motorU_speed_a.isChecked(): self.window_debug.curve_motorU_speed_a.setData(x, graphData[20])
        if self.window_debug.check_motorT_speed_p.isChecked(): self.window_debug.curve_motorT_speed_p.setData(x, graphData[21])
        if self.window_debug.check_motorT_speed_a.isChecked(): self.window_debug.curve_motorT_speed_a.setData(x, graphData[22])

    def sigCall_update_debug(self, list):  #监视AGV参数及绘制曲线
        # print(list)
        for i in range(25):
            self.window_debug.input[i].setText(str(list[i]))

    def sigCall_update_debug2(self, list):  #监视AGV参数及绘制曲线
        print(list)
        print(len(list))

        self.window_debug.input_agv_battery.setText(str(list[0]))
        status = list[1:]
        if status != self.led_oldStatus:  #状态改变
            for i in range(18):
                if i > 1:
                    self.window_debug.label[i].setText(hex(status[i])[2:].zfill(2))
                    if status[i] == 0:
                        self.leds[i].inactive()
                    else:
                        self.leds[i].active()
                else:
                    self.window_debug.label[i].setText(status[i].hex())
                    if status[i] == 0:
                        self.leds[i].inactive()
                    else:
                        self.leds[i].active()

        self.led_oldStatus = status

    def sigCall_update_mecanum(self, x, y, p):  #监视麦克纳姆轮参数
        self.widget_mecanum.input_feedBack_LF.setText(str(p[0]))
        self.widget_mecanum.input_feedBack_RF.setText(str(p[1]))
        self.widget_mecanum.input_feedBack_LB.setText(str(p[2]))
        self.widget_mecanum.input_feedBack_RB.setText(str(p[3]))
        for i in range(4):  #更新6条曲线数据
            self.widget_mecanum.curve[i].setData(x, y[i])

    def sigCall_update_position(self, list):
        self.paint.scatter.setData(list)

    def sigCall_update_PGV_informaton(self, PGV_posX, PGV_posY, PGV_posYaw, PGV_tagNuber):
        self.input_PGV_posX.setText(PGV_posX)
        self.input_PGV_posY.setText(PGV_posY)
        self.input_PGV_posYaw.setText(PGV_posYaw)
        self.input_PGV_tagNuber.setText(PGV_tagNuber)

    def sigCall_update_infomation(self, agv_currentPosX, agv_currentPosY, agv_currentAngle, agv_ultrasonic):
        self.input_agv_currentPosX.setText(agv_currentPosX)  #世界坐标X信息
        self.input_agv_currentPosY.setText(agv_currentPosY)  #世界坐标Y信息
        self.input_agv_curentAngle.setText(agv_currentAngle)  #角度信息
        self.input_agv_ultrasonic.setText(agv_ultrasonic)  #角度信息

    def sigCall_pgB_DmCode(self, i):
        self.widget_tags.pgB_upload.setValue(i)

    #endregion
    #region # 发送数据
    def fuc_serial_sendRaw(self, data):
        global global_s
        try:
            sendData2 = bytes(data)
        except:
            print("发送数据错误")
            print(data)
            return 0
        try:
            if self.widget_connect.check_mode_wifiToUart.isChecked():
                global_s.send(sendData2)
            elif self.rxflag:
                self.ser.write(sendData2)
            elif self.flag_server:
                self.server_thread.signal_send_data.emit(sendData2)
            elif len(self.servers_thread) == 0:
                self.fuc_log('设备未连接')
                return 0
            if (self.widget_log.check_showsSendMessage.isChecked()):  #日志打印发送数据
                self.fuc_log("Tx:" + "  " + HexStrAddSpace(sendData2.hex()))
            for i in range(len(self.servers_thread)):
                self.servers_thread[i].send_message(sendData2)
        except:
            print("发送数据错误")
            print(data)
            return 0

    def fuc_serial_send(self, cmd_id, cmd_data):
        global global_s
        cmd_header = [0x50, 0x43]
        cmd_length = [0x00, 0x00]
        cmd_check = [0x00]
        cmd_end = [0xda]
        cmd_length[1] = len(cmd_data) + 8
        sendData0 = cmd_header + cmd_length + cmd_id + cmd_data
        cmd_check[0] = SumCheck(sendData0, cmd_length[1] - 2)
        sendData1 = sendData0 + cmd_check + cmd_end
        sendData2 = bytes(sendData1)

        if self.widget_connect.check_mode_wifiToUart.isChecked():
            global_s.send(sendData2)
        elif self.rxflag:
            self.ser.write(sendData2)
        elif self.flag_server:
            self.server_thread.signal_send_data.emit(sendData2)
        elif len(self.servers_thread) == 0:
            self.fuc_log('设备未连接')
            return 0
        if (self.widget_log.check_showsSendMessage.isChecked()):  #日志打印发送数据
            self.fuc_log("Tx:" + "  " + HexStrAddSpace(sendData2.hex()))
        for i in range(len(self.servers_thread)):
            self.servers_thread[i].send_message(sendData2)

    #endregion
    #region # 接收数据处理
    def fuc_data_process(self, data):
        self.deviceStatusChecker.last_heartbeat_time = QTime.currentTime()  #收到数据，更新设备在线状态
        if (len(data) == 137):
            self.server_parse_data(data)
        elif (len(data) == 345):
            a = HexStrAddSpace(data.hex())
            self.window_tool_convertMsg.text_raw.setText(a)
            self.window_tool_convertMsg.fuc_convert()
            # self.fuc_log(a)
        elif (self.flag_voice_download):
            self.fuc_voice_downloadFeedback(data)
        else:
            if self.mode_debug == "客户端":
                if self.widget_connect.com_protocol.currentText() == "My":
                    # print(len(data))
                    # a=HexStrAddSpace(data.hex())
                    # print(a)
                    self.fuc_data_restore(data)
                elif self.widget_connect.com_protocol.currentText() == "TCS":
                    print(len(data))
                    print(data)
                    if (len(data) == 137):
                        self.server_parse_data(data)
                    else:
                        self.fuc_data_restore(data)
                        # self.fuc_log('接收长度为%d' % len(data))
            elif self.mode_debug == "串口":
                if self.widget_connect.com_protocol_1.currentText() == "My":
                    self.fuc_data_restore(data)
                elif self.widget_connect.com_protocol_1.currentText() == "TCS":
                    if (len(data) == 137):
                        self.server_parse_data(data)
                    else:
                        self.fuc_data_restore(data)
                        self.fuc_log('接收长度为%d' % len(data))

    def fuc_data_restore(self, data):  # 接收数据还原（去转义）
        pattern = rb'(?<!\\);'  # 如果';'前为'\'则不进行分割
        datalist = [x for x in re.split(pattern, data) if x]  #去掉空''
        for i in datalist:
            pattern = rb'[\\](?=;)'
            i = re.sub(pattern, b"", i)  #如果'\'后面是';'去掉'\'
            if len(i) > 1:
                # print(len(i))
                # if len(i)<30 and i[3]==0x50:
                #     a=HexStrAddSpace(i.hex())
                #     print(a)
                if self.fuc_data_classify(i) == False:
                    # print(i)
                    pass
            else:
                print(data)

    def fuc_data_classify(self, data):  # 接收数据分类
        # print(len(data))
        # try:
        if ((data[0] == 0x50) & (data[1] == 0x43)):  #PC（0x50,0x43）来自于上位机的数据
            self.fuc_data_verify(data)
        elif (data[0] == 0x50):  #接收十六进制数组
            a = HexStrAddSpace(data.hex())
            self.fuc_log(a)
        elif ((data[0] == 0x42) & (data[1] == 0x44)):  #BD（0x42,0x44）来自于控制板的数据
            self.data_from_board = 1
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x4C)):  #来自于清洗机器人M核控制板的数据
            self.data_from_board = 0x434C
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x4F)):  #来自于清洗机工作站M核控制板的数据
            self.data_from_board = 0x434F
            self.fuc_data_verify(data)
        elif ((data[0] == 0x44) & (data[1] == 0x01)):  #来自于清洗机器人驱动板的数据
            self.data_from_board = 0x4401
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x54)):  #来自于CTU上装机构M核的数据
            self.data_from_board = 0x4354
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x57)):  #来自于CTU下装机构M核的数据
            self.data_from_board = 0x4357
            self.fuc_data_verify(data)
        elif ((data[0] == 0x48) & (data[1] == 0x44)):  #BD（0x42,0x44）来自于控制板的数据
            self.fuc_data_verify(data)
            # print(data)
            # a=HexStrAddSpace(data.hex())
            # self.fuc_log(a)
            # print(len(data))
        elif ((data[0] == 0x00) & (data[1] == 0x44)):  #华睿PGV相机原始数据解析
            self.fuc_log(HexStrAddSpace(data.hex()))
            self.fuc_data_verify(data)
        elif ((data[0] == 0x03) & (data[1] == 0x04)):  #倍加福PGV相机原始数据解析
            self.fuc_log(HexStrAddSpace(data.hex()))
            self.fuc_data_verify(data)
        elif ((data[0] == 0x44) & (data[1] == 0x4D)):  #DM显示在二维码界面
            self.widget_tags.info.setText(get_time() + " " + data.decode('utf-8'))
        elif ((data[0] == 0x53) & (data[1] == 0x54)):  #标准机M核控制板
            self.data_from_board = 1
            self.fuc_data_verify(data)
        else:  #接收中文或英文或其它
            if self.widget_log.check_showHex.isChecked():
                a = HexStrAddSpace(data.hex())
            else:
                try:
                    a = data.decode('utf-8')
                    print("utf-8")
                except Exception:
                    try:
                        a = data.decode('gbk')
                        print("gbk")
                    except:
                        a = str(data)
                        print("字符串")
            self.fuc_log(a)
            return False
        return True
        # except:
        #     self.fuc_log("接收数据分类错误")

    def fuc_data_verify(self, data):  # 接收数据验证
        # print(len(data))
        # if(len(data)>3):
        try:
            if ((len(data) > 8) & (len(data) == data[3]) & (data[len(data) - 1] == 0xda)):  #帧头、帧尾、长度都对
                # print("单帧")
                self.fuc_data_apply(data[4:len(data) - 2])
                # a=HexStrAddSpace(data.hex())#查看原始数据
                # self.fuc_log(a)
            elif ((len(data) < 300) & (len(data) > data[3])):  #多帧数据拆分开处理
                processData = data[:data[3]]
                # self.fuc_data_verify(processData) #!!!数据合格分开后，重新验证
                self.fuc_data_verify(processData)  #!!!数据合格分开后，重新验证，不能直接调用fuc_data_apply！！！
                remainData = data[data[3]:]
                # print("多帧")
                # print("总长度"+str(len(data))+'长度'+str(data[3]))
                self.fuc_data_classify(remainData)
                # print("剩余长度"+str(len(remainData)))
            else:
                if (data[len(data) - 1] != 0xda):
                    # self.fuc_log("帧尾错误")
                    try:
                        print(data)
                        a = data.decode('utf-8')
                        print("english")
                    except Exception:
                        try:
                            # a=data.decode('gbk')
                            a = HexStrAddSpace(data.hex())
                            print("中文")
                        except:
                            a = str(data)
                    # self.fuc_log(HexStrAddSpace(data.hex()))
                    self.fuc_log(a)
                else:
                    print("数据格式错误")
                    # self.fuc_data_apply(data[4:len(data)-2])
        except:
            a = HexStrAddSpace(data.hex())
            self.fuc_log(a)

    def fuc_data_apply(self, data):  # 接收数据应用
        if (self.data_from_board == 1):
            if ((data[0] == 0x00) & (data[1] == 0x00)):  #升级进度
                data = data[2:]
                if data[0] == 0xff:
                    self.fuc_log("升级失败")
                else:
                    lenth = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
                    try:
                        self.pgB_upgrade.setValue(lenth)
                    except Exception:
                        self.fuc_log("返回数据错误")
                    if self.fileLen == lenth:
                        self.fuc_log("升级完成")
            elif ((data[0] == 0x12) & (data[1] == 0x01)):  #士腾控制板程序监测
                agv_data1 = data[2:]
                self.thread_debug.fuc_updateData1(agv_data1)
            elif ((data[0] == 0x12) & (data[1] == 0x02)):  #士腾控制板程序监测
                agv_data2 = data[2:]
                self.thread_debug.fuc_updateData2(agv_data2)
            elif ((data[0] == 0x30) & (data[1] == 0x00)):  #上报芯片UID
                data = data[2:]
                uid1 = data[:4][::-1].hex()
                uid2 = data[4:8][::-1].hex()
                uid3 = data[8:12][::-1].hex()
                self.input_license_id1.setText(uid1)
                self.input_license_id2.setText(uid2)
                self.input_license_id3.setText(uid3)
            elif ((data[0] == 0x30) & (data[1] == 0x01)):  #上报芯片XID(UID偏移得到)
                data = data[2:]
                uid1 = data[:4][::-1].hex()
                uid2 = data[4:8][::-1].hex()
                uid3 = data[8:12][::-1].hex()
                self.input_license_xid1.setText(uid1)
                self.input_license_xid2.setText(uid2)
                self.input_license_xid3.setText(uid3)
                # print("设备ID:"+HexStrAddSpace(uid1.hex()+uid2.hex()+uid3.hex()))
                # a=HexStrAddSpace(data.hex())
                # self.fuc_log(a)
            elif ((data[0] == 0x30) & (data[1] == 0x02)):  #上报自身ID
                data = data[2:]
                self.widget_connect.label_device_id.setText(str(data[3] << 8 | data[4]).zfill(4))
                # self.fuc_log("设备ID:"+HexStrAddSpace(data.hex()))
            elif ((data[0] == 0x40) & (data[1] == 0x01)):  #上位机监控数据
                data = data[2:]
                # print(len(data))
                agv_currentLineSpeed = listToFloat(data[0:4])
                agv_currentRateSpeed = listToFloat(data[4:8])
                agv_currentPosX = round(listToFloat(data[16:20]), 3)
                agv_currentPosY = round(listToFloat(data[20:24]), 3)
                agv_currentAngle = round(listToFloat(data[24:28]), 3)

                self.input_agv_curentLineSpeed.setText(str(agv_currentLineSpeed))
                self.input_agv_curentRateSpeed.setText(str(agv_currentRateSpeed))
                self.input_agv_currentPosX.setText(str(agv_currentPosX))  #世界坐标X信息
                self.input_agv_currentPosY.setText(str(agv_currentPosY))  #世界坐标Y信息
                self.input_agv_curentAngle.setText(str(agv_currentAngle))  #角度信息

                self.thread_position.update(agv_currentPosX, agv_currentPosY)  # 更新运动轨迹
                self.thread_angle.fuc_updateAngle(agv_currentAngle)  # 更新角度曲线的数据(通过线程处理数据(数组左移)）
                if (len(data) >= 32):  #超神波距离信息
                    agv_ultrasonic = listToFloat(data[28:32])
                    self.input_agv_ultrasonic.setText(str(agv_ultrasonic))
                if (len(data) >= 34):  #电池电压信息
                    battery_voltage = (data[32] << 8 | data[33]) / 1000
                    self.input_battery_voltage.setText(str(battery_voltage))
                if (len(data) >= 36):  #电池剩余电量信息
                    battery_leftEnergy = data[34] << 8 | data[35]
                    self.input_battery_energy.setText(str(battery_leftEnergy))
                if (len(data) >= 40):  #左电机反馈速度
                    motorLeftSpeed = listToFloat(data[36:40])
                    self.input_motorL_speed.setText(str(motorLeftSpeed))
                if (len(data) >= 44):  #右电机反馈速度
                    motorRightSpeed = listToFloat(data[40:44])
                    self.input_motorR_speed.setText(str(motorRightSpeed))
                if (len(data) >= 48):  #左电机反馈位置
                    motorLeftPosition = listToFloat(data[44:48])
                    self.input_motorL_position.setText(str(motorLeftPosition))
                if (len(data) >= 52):  #右电机反馈位置
                    motorRightPosition = listToFloat(data[48:52])
                    self.input_motorR_position.setText(str(motorRightPosition))
                if (len(data) >= 56):  #旋转电机反馈速度
                    motorRotatorSpeed = listToFloat(data[52:56])
                    self.input_motorT_speed_2.setText(str(motorRotatorSpeed))
                if (len(data) >= 60):  #顶升电机反馈速度
                    motorUpSpeed = listToFloat(data[56:60])
                    self.input_motorU_speed.setText(str(motorUpSpeed))
                    self.input_motorU_speed_2.setText(str(motorUpSpeed))
                if (len(data) >= 64):  #旋转电机反馈位置
                    motorRotatorPosition = listToFloat(data[60:64])
                    self.input_motorT_position.setText(str(motorRotatorPosition))
                    self.input_motorT_position_2.setText(str(motorRotatorPosition))
                if (len(data) >= 68):  #顶升电机反馈位置
                    motorUpPosition = listToFloat(data[64:68])
                    self.input_motorU_position.setText(str(motorUpPosition))
                    self.input_motorU_position_2.setText(str(motorUpPosition))
                if (len(data) >= 71):  #输入状态反馈
                    inputor = data[68:72]
                    for i in range(8):
                        self.led_inputors[i].show(inputor[0] >> (7 - i) & 0x01)
                    for i in range(8, 16):
                        self.led_inputors[i].show(inputor[1] >> (15 - i) & 0x01)
                    for i in range(16, 24):
                        self.led_inputors[i].show(inputor[2] >> (23 - i) & 0x01)
                    for i in range(24, 28):
                        self.led_inputors[i].show(inputor[3] >> (31 - i) & 0x01)
                    # print(inputor)
                    # self.led_inputor_1.show(inputor[0]>>7&0x01)
                    # self.led_inputor_2.show(inputor[0]>>6&0x01)
                    # self.led_inputor_3.show(inputor[0]>>5&0x01)
                    # self.led_inputor_4.show(inputor[0]>>4&0x01)
                    # self.led_inputor_5.show(inputor[0]>>3&0x01)
                    # self.led_inputor_6.show(inputor[0]>>2&0x01)
                    # self.led_inputor_7.show(inputor[0]>>1&0x01)
                    # self.led_inputor_8.show(inputor[0]>>0&0x01)

                    # self.led_inputor_9.show(inputor[1]>>7&0x01)
                    # self.led_inputor_10.show(inputor[1]>>6&0x01)
                    # self.led_inputor_11.show(inputor[1]>>5&0x01)
                    # self.led_inputor_12.show(inputor[1]>>4&0x01)
                    # self.led_inputor_13.show(inputor[1]>>3&0x01)
                    # self.led_inputor_14.show(inputor[1]>>2&0x01)
                    # self.led_inputor_15.show(inputor[1]>>1&0x01)
                    # self.led_inputor_16.show(inputor[1]>>0&0x01)

                    # self.led_inputor_17.show(inputor[2]>>7&0x01)
                    # self.led_inputor_18.show(inputor[2]>>6&0x01)
                    # self.led_inputor_19.show(inputor[2]>>5&0x01)
                    # self.led_inputor_20.show(inputor[2]>>4&0x01)
                    # self.led_inputor_21.show(inputor[2]>>3&0x01)
                    # self.led_inputor_22.show(inputor[2]>>2&0x01)
                    # self.led_inputor_23.show(inputor[2]>>1&0x01)
                    # self.led_inputor_24.show(inputor[2]>>0&0x01)

                    # self.led_inputor_25.show(inputor[3]>>7&0x01)
                    # self.led_inputor_26.show(inputor[3]>>6&0x01)
                    # self.led_inputor_27.show(inputor[3]>>5&0x01)
                    # self.led_inputor_28.show(inputor[3]>>4&0x01)
                # print('hlly')

            elif ((data[0] == 0x40) & (data[1] == 0x02)):  #控制板数据测试预留
                data = data[2:]  #测试预留
            elif ((data[0] == 0x40) & (data[1] == 0x03)):  #更新计算目标角度
                data = data[2:]
                angle = [data[0], data[1], data[2], data[3]]
                angle = listToFloat(angle)
                self.input_agv_targetAngle.setText(str(angle))
            elif ((data[0] == 0x40) & (data[1] == 0x04)):  #更新计算目标距离
                data = data[2:]
                targetDistance = [data[0], data[1], data[2], data[3]]
                targetDistance = listToFloat(targetDistance)
                self.input_agv_targetDistance.setText(str(targetDistance))
            elif ((data[0] == 0x40) & (data[1] == 0x05)):  #更新PGV相机信息
                data = data[2:]
                # x
                PGV_posX = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
                if (data[0] >> 7): PGV_posX = PGV_posX - 0x100000000
                # y
                PGV_posY = data[4] << 8 | data[5]
                if (data[4] >> 7): PGV_posY = PGV_posY - 0x10000
                # w
                PGV_posYaw = data[6] << 8 | data[7]
                if (data[6] >> 7): PGV_posYaw = PGV_posYaw - 0x10000
                PGV_posYaw = round(PGV_posYaw / 10, 1)
                # number
                PGV_tagNuber = data[8] << 24 & 0xff | data[9] << 16 & 0xff | data[10] << 8 & 0xff | data[11] & 0xff

                # 更新PGV数据
                self.input_PGV_posX.setText(str(PGV_posX))
                self.input_PGV_posY.setText(str(PGV_posY))
                self.input_PGV_posYaw.setText(str(PGV_posYaw))
                self.input_PGV_tagNuber.setText(str(PGV_tagNuber))
                # 更新角度曲线的数据
                # self.angleCurve.y1[:-1] = self.angleCurve.y1[1:]
                # self.angleCurve.y1[-1] = PGV_posYaw
                # self.angleCurve.plot1.setData(self.angleCurve.x, self.angleCurve.y1)
            elif ((data[0] == 0x40) & (data[1] == 0x06)):  #更新料架相机数据
                data = data[2:]
                # x
                PGV_posX = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
                if (data[0] >> 7): PGV_posX = PGV_posX - 0x100000000
                # y
                PGV_posY = data[4] << 8 | data[5]
                if (data[4] >> 7): PGV_posY = PGV_posY - 0x10000
                # w
                PGV_posYaw = data[6] << 8 | data[7]
                if (data[6] >> 7): PGV_posYaw = PGV_posYaw - 0x10000
                PGV_posYaw = round(PGV_posYaw / 10, 1)
                # number
                PGV_tagNuber = data[8] << 24 & 0xff | data[9] << 16 & 0xff | data[10] << 8 & 0xff | data[11] & 0xff

                # 更新PGV数据
                self.input_PGV_posX_up.setText(str(PGV_posX))
                self.input_PGV_posY_up.setText(str(PGV_posY))
                self.input_PGV_posYaw_up.setText(str(PGV_posYaw))
                self.input_PGV_tagNuber_up.setText(str(PGV_tagNuber))

                # 计算料架角度
                angle_down = float(self.input_PGV_posYaw.text())
                angle_up = float(self.input_PGV_posYaw_up.text())
                angle = angle_up + angle_down
                angle = round(angle, 2)
                if angle > 360: angle = angle - 360
                elif angle < -360: angle = angle + 360
                self.lineEdit_rack_angle.setText(str(angle))
                # agv_currentPosX=listToFloat(data[0:4])
                # agv_currentPosY=listToFloat(data[4:8])
                #更新运动轨迹
                # if((self.last_agv_currentPosX!=agv_currentPosX)|(self.last_agv_currentPosY!=agv_currentPosY)):
                #     self.paint.dotdata.append({'pos': (agv_currentPosX, agv_currentPosY),'brush': 'r','pen':'r'})  # 新增点
                #     self.paint.scatter.setData(self.paint.dotdata)
                # self.last_agv_currentPosX=agv_currentPosX
                # self.last_agv_currentPosY=agv_currentPosY
            elif ((data[0] == 0x40) & (data[1] == 0x07)):  #监测陀螺仪原始数据
                imuRawData = data[2:]
                self.thread_angle.fuc_updateImuRaw(imuRawData)  # 更新角度曲线的数据(通过线程处理数据(数组左移)）

            elif ((data[0] == 0x40) & (data[1] == 0x10)):  #读取flash参数1-7
                data = data[2:]
                if len(data) == (len(self.agv_parameter)) * 4:  #每个参数占4个字节
                    for i in range(len(self.agv_parameter)):
                        found_widget = self.widget_agvParameter.findChild(QLineEdit, self.agv_parameter[i])  #根据字符串查找widget中的对象
                        if i == 0:  #第一个参数为版本号，是整数不是小数
                            found_widget.setText(str(data[4 * i]))
                        else:
                            found_widget.setText(str(listToFloat(data[4 * i:4 + 4 * i])))
                    self.fuc_log('参数读取完成')
                    self.btn_read_flashParameter.setText("读取参数")
                else:
                    print('接收长度错误')
            elif ((data[0] == 0x40) & (data[1] == 0x11)):  #读取flash参数8-14
                data = data[2:]
                # print(data)
                imu_integralGain = listToFloat(data[0:4])
                imu_GyroOffset_Z = listToFloat(data[4:8])
                agv_angleRangeOffset = listToFloat(data[8:12])
                agv_distanceRangeOffset = listToFloat(data[12:16])
                factor_rectify = listToFloat(data[16:20])
                rectifyAngle = listToFloat(data[20:24])
                self.input_imu_integralGain.setText(str(imu_integralGain))
                self.input_imu_offsetZ.setText(str(imu_GyroOffset_Z))
                self.input_angleRangeOffset.setText(str(agv_angleRangeOffset))
                self.input_distanceRangeOffset.setText(str(agv_distanceRangeOffset))
                self.input_factor_rectify.setText(str(factor_rectify))
                self.input_rectifyAngle.setText(str(rectifyAngle))
                self.btn_read_flashParameter.setText("读取参数")
            elif ((data[0] == 0x40) & (data[1] == 0x12)):  #读取flash参数15-21
                data = data[2:]
                # print(data)
                leftWheel_compensation = listToFloat(data[0:4])
                rightWheel_compensation = listToFloat(data[4:8])
                leftWheel_offset = listToFloat(data[8:12])
                rightWheel_offset = listToFloat(data[12:16])
                self.input_LW_compensation.setText(str(leftWheel_compensation))
                self.input_RW_compensation.setText(str(rightWheel_compensation))
                self.input_LW_offset.setText(str(leftWheel_offset))
                self.input_RW_offset.setText(str(rightWheel_offset))
            elif ((data[0] == 0x40) & (data[1] == 0x20)):  #接收二维码标定数据
                data = data[2:]
                DM_tagNumber = listToInt32_little(data[0:4])
                DM_posX = listToFloat(data[4:8])
                DM_posY = listToFloat(data[8:12])
                DM_ErrorAngle = listToFloat(data[12:])

                # 创建新页面
                if self.DMcode_number % self.page_codeNum == 0:
                    self.new_page = QWidget()
                    self.grid_layout = QGridLayout()
                    self.new_page.setLayout(self.grid_layout)
                    self.widget_tags.stack_tags.addWidget(self.new_page)
                    # labelList=["","序号","二维码编码","坐标X","坐标Y","补偿角度"]
                    labelList = ["", "序号", "二维码编码", "坐标X", "坐标Y", "补偿角度"]
                    for i in range(3):
                        for j in range(6):
                            label = QLabel(labelList[j])
                            self.grid_layout.addWidget(label, 0, 6 * i + j)

                # 创建输入框
                check = QCheckBox()
                check.setChecked(True)
                self.checkBoxs.append(check)
                label = QLabel(str(self.DMcode_number + int(self.widget_tags.input_start_address.text())))
                self.dmcode_labels.append(label)
                self.grid_layout.addWidget(check, self.DMcode_number % self.page_codeNum // 3 + 1, 6 * (self.DMcode_number % self.page_codeNum % 3))
                self.grid_layout.addWidget(label, self.DMcode_number % self.page_codeNum // 3 + 1,
                                           6 * (self.DMcode_number % self.page_codeNum % 3) + 1)
                for j in range(4):
                    line_edit = QLineEdit(self)
                    line_edit.setObjectName(f'line_edit_{4*self.DMcode_number+j}')
                    self.line_edits.append(line_edit)
                    self.grid_layout.addWidget(line_edit, self.DMcode_number % self.page_codeNum // 3 + 1,
                                               6 * (self.DMcode_number % self.page_codeNum % 3) + j + 2)

                self.line_edits[4 * self.DMcode_number].setText(str(DM_tagNumber))
                self.line_edits[4 * self.DMcode_number + 1].setText(str(DM_posX))
                self.line_edits[4 * self.DMcode_number + 2].setText(str(DM_posY))
                self.line_edits[4 * self.DMcode_number + 3].setText(str(DM_ErrorAngle))
                self.DMcode_number += 1
            elif ((data[0] == 0x40) & (data[1] == 0x40)):  #路径跟踪参数
                data = data[2:]
                K_Cross = listToFloat(data[0:4])
                K_LowVelo = listToFloat(data[4:8])
                K_trace_angle = listToFloat(data[8:12])
                K_cross_angle = listToFloat(data[12:16])
                self.lineEdit_K_Cross.setText(str(K_Cross))
                self.lineEdit_K_LowVelo.setText(str(K_LowVelo))
                self.lineEdit_K_trace_angle.setText(str(K_trace_angle))
                self.lineEdit_K_cross_angle.setText(str(K_cross_angle))
            elif ((data[0] == 0x40) & (data[1] == 0x60)):  #差速轮速度
                data = data[2:]
                currentLineSpeed = listToFloat(data[0:4])
                currentRadSpeed = listToFloat(data[4:8])
                self.input_agv_curentLineSpeed.setText(str(currentLineSpeed))
                self.input_agv_curentRateSpeed.setText(str(currentRadSpeed))
            elif ((data[0] == 0x42) & (data[1] == 0x00)):  #更新扩展输入口状态
                self.check_input_1.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.check_input_2.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.check_input_3.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.check_input_4.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.check_input_5.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.check_input_6.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.check_input_7.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.check_input_8.setChecked(True if ((data[2] & 0x80) > 0) else False)
            elif ((data[0] == 0x42) & (data[1] == 0x09)):  #CAN通信反馈(电机、IMU)
                self.fuc_log(HexStrAddSpace(data[2:].hex()))
                canId = data[2] << 8 | data[3]
                canData = data[4:]
                if (canId == 0x700 + int(self.input_motor_nodeId.text())):
                    if (canData[0] == 0x00): self.label_motor_heartState.setText("启动状态")
                    if (canData[0] == 0x02): self.label_motor_heartState.setText("连接状态")
                    if (canData[0] == 0x04): self.label_motor_heartState.setText("停止状态")
                    if (canData[0] == 0x05): self.label_motor_heartState.setText("操作状态")
                    if (canData[0] == 0x7F):
                        self.label_motor_heartState.setText("预操作状态")
                        self.label_motor_active.setText("已激活")
                # if(canId==0x180+int(self.input_motor_nodeId.text())):
                #     speed=canData[1]<<8|canData[0]
                #     if(canData[1]>>7):speed-=0x10000
                #     self.label_motor_speed.setText(str(int(speed))+'rpm')
                if (canId == 0x585):  # 陀螺仪数据
                    if (canData[3] == 0X01):
                        angle = [canData[4], canData[5], canData[6], canData[7]]
                        angle = listToFloat(angle)
                        self.input_agv_curentAngle.setText(str(angle))
                    if (canData[3] == 0X02):
                        anglerRate = canData[4] + canData[5] << 8 + canData[6] << 16 + canData[7] << 24

                        print(anglerRate)
                        # self.input_imu_angleRate.setText(str(anglerRate)+'rad/s')
            elif ((data[0] == 0x42) & (data[1] == 0x20)):  #处理读码相机数据
                self.sigCall_process_PGVData(data[2:])
            elif ((data[0] == 0x70) & (data[1] == 0x01)):  #麦克纳姆小车_轮速
                data = data[2:]
                self.thread_mecanum.fuc_updateData(data)
            elif ((data[0] == 0x70) & (data[1] == 0x02)):  #麦克纳姆小车_PID参数
                data = data[2:]
                for i in range(12):
                    self.pid_inputs[i].setText(str(listToFloat_B(data[4 * i:4 * i + 4])))
            elif ((data[0] == 0x80) & (data[1] == 0x01)):  #手柄采样数据
                data = data[2:]
                lx = listToInt16(data[0:2])
                ly = listToInt16(data[2:4])
                rx = listToInt16(data[4:6])
                ry = listToInt16(data[6:8])
                self.widget_handle.progress_scale_LX.setValue(lx)
                self.widget_handle.progress_scale_LY.setValue(ly)
                self.widget_handle.progress_scale_RX.setValue(rx)
                self.widget_handle.progress_scale_RY.setValue(ry)
                self.widget_handle.line_scale_LX.setText(str(lx))
                self.widget_handle.line_scale_LY.setText(str(ly))
                self.widget_handle.line_scale_RX.setText(str(rx))
                self.widget_handle.line_scale_RY.setText(str(ry))
                # print("lx: ",lx,"ly: ",ly,"rx: ",rx,"ry: ",ry)
                if self.flag_calibrate_scale:
                    lx_min = int(self.widget_handle.line_scale_LXmin.text())
                    lx_max = int(self.widget_handle.line_scale_LXmax.text())
                    ly_min = int(self.widget_handle.line_scale_LYmin.text())
                    ly_max = int(self.widget_handle.line_scale_LYmax.text())
                    rx_min = int(self.widget_handle.line_scale_RXmin.text())
                    rx_max = int(self.widget_handle.line_scale_RXmax.text())
                    ry_min = int(self.widget_handle.line_scale_RYmin.text())
                    ry_max = int(self.widget_handle.line_scale_RYmax.text())

                    if lx < lx_min: self.widget_handle.line_scale_LXmin.setText(str(lx))
                    if lx > lx_max: self.widget_handle.line_scale_LXmax.setText(str(lx))
                    if ly < ly_min: self.widget_handle.line_scale_LYmin.setText(str(ly))
                    if ly > ly_max: self.widget_handle.line_scale_LYmax.setText(str(ly))
                    if rx < rx_min: self.widget_handle.line_scale_RXmin.setText(str(rx))
                    if rx > rx_max: self.widget_handle.line_scale_RXmax.setText(str(rx))
                    if ry < ry_min: self.widget_handle.line_scale_RYmin.setText(str(ry))
                    if ry > ry_max: self.widget_handle.line_scale_RYmax.setText(str(ry))

            elif ((data[0] == 0x80) & (data[1] == 0x02)):  #手柄采样数据
                data = data[2:]
                lx_min = listToInt16(data[0:2])
                ly_min = listToInt16(data[2:4])
                rx_min = listToInt16(data[4:6])
                ry_min = listToInt16(data[6:8])
                lx_max = listToInt16(data[8:10])
                ly_max = listToInt16(data[10:12])
                rx_max = listToInt16(data[12:14])
                ry_max = listToInt16(data[14:16])
                # self.custom_rect1.setColorRange(lx_min,lx_max)
                # print(lx_min,ly_min)
                # print(adc1,adc2,adc3,adc4)
            else:
                a = HexStrAddSpace(data.hex())
                self.fuc_log(a)
        elif (self.data_from_board == 0x4401):  #清洗机器人驱动板
            if ((data[0] == 0x42) & (data[1] == 0x00)):  #更新扩展输入口状态
                self.check_driver_input_1.setChecked(True if ((data[2] & 0x80) > 0) else False)
                self.check_driver_input_2.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.check_driver_input_3.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.check_driver_input_4.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.check_driver_input_5.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.check_driver_input_6.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.check_driver_input_7.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.check_driver_input_8.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.check_driver_input_9.setChecked(True if ((data[3] & 0x80) > 0) else False)
                self.check_driver_input_10.setChecked(True if ((data[3] & 0x40) > 0) else False)
                self.check_driver_input_11.setChecked(True if ((data[3] & 0x20) > 0) else False)
                self.check_driver_input_12.setChecked(True if ((data[3] & 0x10) > 0) else False)
        elif (self.data_from_board == 0x434C):  #清洗机器人M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x60)):
                data = data[2:]
                # 差速轮速度
                currentLineSpeed = listToFloat(data[0:4])
                currentRadSpeed = listToFloat(data[4:8])
                self.widget_clean.input_curentLineSpeed.setText(str(currentLineSpeed))
                self.widget_clean.input_curentRadSpeed.setText(str(currentRadSpeed))
            if ((data[0] == 0x40) & (data[1] == 0x70)):
                #电量百分比 %
                self.widget_clean.input_battery_energy.setText(str(data[2]))
                #电池电压
                self.widget_clean.input_battery_voltage.setText(str(data[3] << 16 | data[4]))
                #污水水位
                self.widget_clean.checkBox_dirtyWater1.setChecked(True if ((data[5] & 0x80) > 0) else False)
                self.widget_clean.checkBox_dirtyWater2.setChecked(True if ((data[5] & 0x40) > 0) else False)
                self.widget_clean.checkBox_dirtyWater3.setChecked(True if ((data[5] & 0x20) > 0) else False)
                self.widget_clean.checkBox_dirtyWater4.setChecked(True if ((data[5] & 0x10) > 0) else False)
                self.widget_clean.checkBox_dirtyWater5.setChecked(True if ((data[5] & 0x08) > 0) else False)
                #清水水位
                self.widget_clean.checkBox_cleanWater1.setChecked(True if ((data[6] & 0x80) > 0) else False)
                self.widget_clean.checkBox_cleanWater2.setChecked(True if ((data[6] & 0x40) > 0) else False)
                self.widget_clean.checkBox_cleanWater3.setChecked(True if ((data[6] & 0x20) > 0) else False)
                self.widget_clean.checkBox_cleanWater4.setChecked(True if ((data[6] & 0x10) > 0) else False)
                self.widget_clean.checkBox_cleanWater5.setChecked(True if ((data[6] & 0x08) > 0) else False)
                #推杆位置
                self.widget_clean.checkBox_scrub_origin.setChecked(True if ((data[7] & 0x80) > 0) else False)
                self.widget_clean.checkBox_scrub_end.setChecked(True if ((data[7] & 0x40) > 0) else False)
                self.widget_clean.checkBox_scraper_origin.setChecked(True if ((data[7] & 0x20) > 0) else False)
                self.widget_clean.checkBox_scraper_end.setChecked(True if ((data[7] & 0x10) > 0) else False)
                #超声波避障信息
                self.widget_clean.checkBox_ultrasonic1.setChecked(True if ((data[8] & 0x80) > 0) else False)
                self.widget_clean.checkBox_ultrasonic2.setChecked(True if ((data[8] & 0x40) > 0) else False)
                self.widget_clean.checkBox_ultrasonic3.setChecked(True if ((data[8] & 0x20) > 0) else False)
                self.widget_clean.checkBox_ultrasonic4.setChecked(True if ((data[8] & 0x10) > 0) else False)
                self.widget_clean.checkBox_ultrasonic5.setChecked(True if ((data[8] & 0x08) > 0) else False)
                self.widget_clean.checkBox_ultrasonic6.setChecked(True if ((data[8] & 0x04) > 0) else False)
                self.widget_clean.checkBox_ultrasonic7.setChecked(True if ((data[8] & 0x02) > 0) else False)
                self.widget_clean.checkBox_ultrasonic8.setChecked(True if ((data[8] & 0x01) > 0) else False)
                #前扫描仪信息
                self.widget_clean.checkBox_radarF1.setChecked(True if ((data[9] & 0x80) > 0) else False)
                self.widget_clean.checkBox_radarF2.setChecked(True if ((data[9] & 0x40) > 0) else False)
                self.widget_clean.checkBox_radarF3.setChecked(True if ((data[9] & 0x20) > 0) else False)
                #按钮及反馈信息
                self.widget_clean.checkBox_emergency1.setChecked(True if ((data[10] & 0x80) > 0) else False)
                self.widget_clean.checkBox_emergency2.setChecked(True if ((data[10] & 0x40) > 0) else False)
                self.widget_clean.checkBox_foward.setChecked(True if ((data[10] & 0x20) > 0) else False)
                self.widget_clean.checkBox_backward.setChecked(True if ((data[10] & 0x10) > 0) else False)
                self.widget_clean.checkBox_reset.setChecked(True if ((data[10] & 0x08) > 0) else False)
                self.widget_clean.checkBox_brake.setChecked(True if ((data[10] & 0x04) > 0) else False)
                self.widget_clean.checkBox_power_driver.setChecked(True if ((data[10] & 0x02) > 0) else False)
                self.widget_clean.checkBox_ready_radar.setChecked(True if ((data[10] & 0x01) > 0) else False)
        elif (self.data_from_board == 0x434F):  #清洗机工作站M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x70)):
                self.widget_clean.checkBox_station_emergency1.setChecked(True if ((data[2] & 0x80) > 0) else False)
                self.widget_clean.checkBox_station_emergency2.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.widget_clean.checkBox_station_clean.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.widget_clean.checkBox_station_auto.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.widget_clean.checkBox_station_manual.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.widget_clean.checkBox_station_extend.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.widget_clean.checkBox_station_retract.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.widget_clean.checkBox_station_extendIn.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.widget_clean.checkBox_station_retractIn.setChecked(True if ((data[3] & 0x80) > 0) else False)
                self.widget_clean.checkBox_station_cleanLevel.setChecked(True if ((data[3] & 0x40) > 0) else False)
                self.widget_clean.checkBox_station_sewageLevel.setChecked(True if ((data[3] & 0x20) > 0) else False)
                self.widget_clean.checkBox_station_agvIn.setChecked(True if ((data[3] & 0x10) > 0) else False)
                self.widget_clean.checkBox_station_charge.setChecked(True if ((data[3] & 0x08) > 0) else False)
                self.widget_clean.checkBox_station_fault.setChecked(True if ((data[3] & 0x04) > 0) else False)
        elif (self.data_from_board == 0x4354):  #CTU上装机构M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x70)):
                self.widget_ctu_up.checkBox_limit_up.setChecked(True if ((data[2] & 0x80) > 0) else False)
                self.widget_ctu_up.checkBox_limit_down.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.widget_ctu_up.checkBox_limit_left.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.widget_ctu_up.checkBox_limit_right.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.widget_ctu_up.checkBox_limit_extend.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.widget_ctu_up.checkBox_limit_retract.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.widget_ctu_up.checkBox_limit_clampL.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.widget_ctu_up.checkBox_limit_clampR.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.widget_ctu_up.checkBox_limit_chainL.setChecked(True if ((data[3] & 0x80) > 0) else False)
                self.widget_ctu_up.checkBox_limit_chainR.setChecked(True if ((data[3] & 0x40) > 0) else False)
                self.widget_ctu_up.checkBox_limit_offsetL.setChecked(True if ((data[3] & 0x20) > 0) else False)
                self.widget_ctu_up.checkBox_limit_offsetR.setChecked(True if ((data[3] & 0x10) > 0) else False)
            if ((data[0] == 0x40) & (data[1] == 0x71)):  #各电机位置上报
                data = data[2:]
                if (len(data) >= 4):  #升降电机
                    motor2_postion = listToInt32_little(data[0:4])
                    self.widget_ctu_up.input_motor2_position.setText(str(motor2_postion))
                if (len(data) >= 8):
                    motor3_postion = listToInt32_little(data[4:8])
                    self.widget_ctu_up.input_motor3_position.setText(str(motor3_postion))
                if (len(data) >= 12):
                    motor4_postion = listToInt32_little(data[8:12])
                    self.widget_ctu_up.input_motor4_position.setText(str(motor4_postion))
                if (len(data) >= 16):
                    motor5_postion = listToInt32_little(data[12:16])
                    self.widget_ctu_up.input_motor5_position.setText(str(motor5_postion))
            if ((data[0] == 0x40) & (data[1] == 0x72)):  #各电机速度上报
                data = data[2:]
                if (len(data) >= 4):  #升降电机
                    motor2_speed = listToInt32_little(data[0:4])
                    self.widget_ctu_up.input_motor2_speed.setText(str(motor2_speed))
                if (len(data) >= 8):
                    motor3_speed = listToInt32_little(data[4:8])
                    self.widget_ctu_up.input_motor3_speed.setText(str(motor3_speed))
                if (len(data) >= 12):
                    motor4_speed = listToInt32_little(data[8:12])
                    self.widget_ctu_up.input_motor4_speed.setText(str(motor4_speed))
                if (len(data) >= 16):
                    motor5_speed = listToInt32_little(data[12:16])
                    self.widget_ctu_up.input_motor5_speed.setText(str(motor5_speed))
        elif (self.data_from_board == 0x4357):  #CTU下装机构M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x70)):
                #输入口状态
                self.widget_ctu_up.checkBox_in_emergency.setChecked(True if ((data[2] & 0x80) > 0) else False)
                self.widget_ctu_up.checkBox_in_collision.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarF1.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarF2.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarF3.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarB1.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarB2.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarB3.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.widget_ctu_up.checkBox_in_mannual.setChecked(True if ((data[3] & 0x80) > 0) else False)
                self.widget_ctu_up.checkBox_in_reset.setChecked(True if ((data[3] & 0x40) > 0) else False)
                self.widget_ctu_up.checkBox_in_brake.setChecked(True if ((data[3] & 0x20) > 0) else False)
                self.widget_ctu_up.checkBox_in_walk.setChecked(True if ((data[3] & 0x10) > 0) else False)
                self.widget_ctu_up.checkBox_in_lift.setChecked(True if ((data[3] & 0x08) > 0) else False)

                #电量
                voltage = (data[4] * 256 + data[5]) / 1000
                energy = (data[6] * 256 + data[7]) / 1000
                self.widget_ctu_up.input_battery_voltage.setText(str(voltage))
                self.widget_ctu_up.input_battery_energy.setText(str(energy))
        elif (self.data_from_board == 0x5354):  #CTU下装机构M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x60)):
                data = data[2:]
                # 差速轮速度
                currentLineSpeed = listToFloat(data[0:4])
                currentRadSpeed = listToFloat(data[4:8])
                self.input_agv_curentLineSpeed.setText(str(currentLineSpeed))
                self.input_agv_curentRateSpeed.setText(str(currentRadSpeed))

    def server_parse_data(self, data):  # 解析调度系统数据
        # print(len(data))
        Distribution_code = data[12:16]
        if Distribution_code == b'AFLF':  #验证接收数据
            CMD_STATE = list(data[24:64])
            # for i in range(20):#数据存储大小端交换
            #     CMD_STATE[2*i:2*i+2]=CMD_STATE[2*i:2*i+2][::-1]
            CMD_STATE_note = ["未开始", " 开始 ", "执行中", " 完成 ", " 错误 "]
            CMD_STATE_color = ["grey", " black", "blue", " green", " red"]
            self.flag_doneCMD = True
            for i in range(10):
                # cmdNum=CMD_STATE[0+4*i]*16+CMD_STATE[1+4*i]#data[0]data[1]为返回的状态序号
                cmdNum = CMD_STATE[0 + 4 * i] << 8 | CMD_STATE[1 + 4 * i]  #data[0]data[1]为返回的状态序号
                cmdState = CMD_STATE[3 + 4 * i]
                widget = self.gridLayout_25.itemAtPosition(i, 0).widget()
                widget.setText(str(cmdNum))
                widget = self.gridLayout_25.itemAtPosition(i, 1).widget()
                widget.setText(CMD_STATE_note[cmdState])
                widget.setStyleSheet("color:" + CMD_STATE_color[cmdState])  #根据状态号设置状态颜色
                if cmdNum != 0 and cmdState != 3:  #全部有效指令已完成
                    self.flag_doneCMD = False
                # for j in range(self.num_cmd):
                #     widget = self.layout_cmd.itemAt(11+(0)*self.num_cmd+j).widget()
                #     if widget.isChecked():#判断复选框是否被选中
                #         widget = self.layout_cmd.itemAt(11+(1)*self.num_cmd+j).widget()
                #         if widget.text()==str(cmdNum):#判断命令号相同,默认初始反馈的10条命令命令号均为0时，下发命令号为0的命令，状态会被其他命令号为0的状态覆盖
                #             widget = self.layout_cmd.itemAt(11+(10)*self.num_cmd+j).widget()
                #             widget.setText(CMD_STATE_note[cmdState])#data[3]为返回的状态命令位，根据状态号设置状态名字
                #             widget.setStyleSheet("color:"+CMD_STATE_color[cmdState])#根据状态号设置状态颜色
                # if cmdState==1:
                # if i==9:
                #     print("cmdState:",cmdState)
                #     if cmdState==3:
                #         print("执行完成")
                #         if self.checkBox_cmdCycle.isChecked():
                #             print("循环下发")

            if self.flag_doneCMD and self.checkBox_cmdCycle.isChecked():
                print("指令全部完成")
                self.count_cmdDone += 1
                if self.count_cmdDone >= 2:
                    self.count_cmdDone = 0
                    widget = self.layout_cmd.itemAt(0).widget()
                    if widget.isChecked():
                        widget.setChecked(False)
                    else:
                        widget.setChecked(True)

                    if widget.isChecked():
                        widget.setChecked(False)
                    else:
                        widget.setChecked(True)

                    self.fuc_sendCmd_TCS()
                # time.sleep(1)

            posX = listToInt32(data[64:68])  #当前位置X
            posY = listToInt32(data[68:72])  #当前位置Y
            angle = listToInt32(data[72:76])  #当前角度
            speed = listToInt32(data[76:80])  #当前线速度
            nav_mode = 0  #导航模式
            lift = 0  #顶升
            tab_degree = 0  #转盘角度
            reserve_degree = 0  #预留角度
            RFID_last = listToUint16(data[88:90])  #上一个RFID
            # tag_number=listToInt32(data[91:93]) #当前RFID
            # RFID=listToUint16(data[90:92]) #当前RFID
            RFID = data[90] << 8 | data[91]  #当前RFID
            speed_x = 0  #X方向速度
            speed_y = 0  #X方向速度
            rateSpeed = listToInt32(data[100:104])  #当前角速度
            Errors = []  #错误位
            Warn = []  #警告 位
            Status = []  #状态位
            battery = listToInt32(data[124:126])  #当前电池电量百分比
            capacity = 0  #当前电容电量百分比
            odometer = 0  #里程计
            BAT_health = 0  #电池健康度
            Reserved = 0  #预留
            self.input_fb_posX.setText(str(posX))
            self.input_fb_posY.setText(str(posY))
            self.input_fb_angle.setText(str(angle))
            self.input_fb_speed.setText(str(speed))
            self.input_fb_battery.setText(str(battery) + '%')
            self.input_fb_rateSpeed.setText(str(rateSpeed))
            self.input_fb_Status.setText(str(Status))
            self.input_fb_Errors.setText(str(Errors))
            self.input_fb_RFID.setText(str(RFID))

            #更新运动轨迹
            agv_currentPosX = posX / 1000
            agv_currentPosY = posY / 1000
            self.thread_position.update(agv_currentPosX, agv_currentPosY)  # 更新运动轨迹
            # if((self.last_agv_currentPosX!=agv_currentPosX)|(self.last_agv_currentPosY!=agv_currentPosY)):
            #     self.paint.dotdata.append({'pos': (agv_currentPosX, agv_currentPosY),'brush': pg.mkBrush('g'),'pen':pg.mkPen(color='g')})  # 设置每个点的颜色
            #     self.paint.scatter.setData(self.paint.dotdata)
            # self.last_agv_currentPosX=agv_currentPosX
            # self.last_agv_currentPosY=agv_currentPosY
            # print(data[64],data[65],data[66],data[67])
        # flag_cmd_cycle=1
        # for i in range(self.cmd_count):
        #     if CMD_STATE[2+4*i]!=4:
        #         flag_cmd_cycle=0
        # if (flag_cmd_cycle==1)&(self.check_cmd_cycle.isChecked()):
        #     self.fuc_sendCmd_all()
        # AGV_STATE=data[64:96]
        # # self.signal.print.emit('收到数据: %s' % AGV_STATE)
        # self.input_fb_posX.setText(str(int.from_bytes(AGV_STATE[:4], byteorder='little',signed=True)/10))
        # self.input_fb_posY.setText(str(int.from_bytes(AGV_STATE[4:8], byteorder='little',signed=True)/10))
        # self.input_fb_angle.setText(str(int.from_bytes(AGV_STATE[8:10], byteorder='little',signed=True)/10))
        # self.input_fb_speed.setText(str(int.from_bytes(AGV_STATE[12:16], byteorder='little',signed=True)/10))
        # self.input_fb_angle.setText(str(struct.unpack('<H', AGV_STATE[8:10])[0]))

    #endregion
    #region # AGV运动控制
    def fuc_agv_stop(self):  #停止
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = 0
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)
        time.sleep(0.1)
        self.fuc_stop_RU()  #停止转盘和顶升

    def fuc_agv_moveForward(self):  #前进
        if self.protocol == 'My':
            cmd_id = [0x40, 0x60]
            lineSpeed = float(self.input_agv_lineSpeed.text())
            rateSpeed = 0
            cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
            self.fuc_serial_send(cmd_id, cmd_data)
            self.add_keyDot(lineSpeed, rateSpeed)
        elif self.protocol == 'TCS':
            self.cmdNum += 1
            self.fuc_sendUiCmd_TCS(ID=self.cmdNum, name="GP", p1=300)

    def fuc_agv_moveBack(self):  #后退
        cmd_id = [0x40, 0x60]
        lineSpeed = -float(self.input_agv_lineSpeed.text())
        rateSpeed = 0
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_rotateCW(self):  #正转
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = -float(self.input_agv_rateSpeed.text())
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_rotateCCW(self):  #反转
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = float(self.input_agv_rateSpeed.text())
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_run(self):  #曲线运行
        cmd_id = [0x40, 0x60]
        lineSpeed = float(self.input_agv_lineSpeed.text())
        rateSpeed = float(self.input_agv_rateSpeed.text())
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_runBack(self):  #反向曲线运行
        cmd_id = [0x40, 0x60]
        lineSpeed = -float(self.input_agv_lineSpeed.text())
        rateSpeed = -float(self.input_agv_rateSpeed.text())
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_toAngle(self):  #旋转到指定角度
        cmd_id = [0x40, 0x32]
        angle = int(float(self.input_agv_targetAngle.text()) * 10)
        cmd_data = [angle >> 8 & 0xff, angle & 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_agv_toDistance(self):  #运行指定距离
        cmd_id = [0x40, 0x33]
        distance = round(float(self.input_agv_targetDistance.text()), 2)
        cmd_data = floatToList(distance)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_agv_toPos(self):  #运行到指定点
        cmd_id = [0x40, 0x34]
        agv_targetPosX = floatToList(round(float(self.input_agv_targetPosX.text()), 5))
        agv_targetPosY = floatToList(round(float(self.input_agv_targetPosY.text()), 5))
        cmd_data = agv_targetPosX + agv_targetPosY
        self.fuc_serial_send(cmd_id, cmd_data)

        self.paint.dotdata.append({
            'pos': (round(float(self.input_agv_targetPosX.text()), 2), round(float(self.input_agv_targetPosY.text()), 2)),
            'brush': pg.mkBrush('r')
        })
        self.paint.scatter.setData(self.paint.dotdata)

    def fuc_agv_resetPos(self):  #位置坐标复位
        cmd_id = [0x40, 0x35]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_agv_resetAngle(self):  #角度复位
        # cmd_id=[0x40,0x21]
        # cmd_data=[0x00,0x00]
        # self.fuc_serial_send(cmd_id,cmd_data)
        cmd_id = [0x40, 0x22]
        # angle=int(float(self.input_angleRectify.text())*10)
        angle = 0
        cmd_data = [angle >> 8, angle & 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_obstacle_ignore(self):  # 配置雷达避障
        cmd_id = [0x40, 0x55]
        cmd_data = [0x00, 0x00]
        if (self.check_obstacle_ignore.isChecked()):
            cmd_data = [0x01, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_watch_agvState(self):  # AGV状态监测开启/关闭
        cmd_id = [0x80, 0x02]
        cmd_data = [0x01, 0x00]
        if (self.check_watch_agvState.isChecked()):
            cmd_data = [0x01, 0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_enMotor(self):
        cmd_id = [0x40, 0x11]
        cmd_data = [0xff, 0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motor_protect(self):
        cmd_id = [0x40, 0x12]
        cmd_data = [0xff, 0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 转盘及升降机构控制
    def fuc_stop_RU(self):
        cmd_id = [0x40, 0x70]
        cmd_data = [0xff, 0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_rotator_CW(self):
        cmd_id = [0x40, 0x71]
        speed = abs(int(self.input_motorT_speed_T.text()))
        if speed > 3000:
            speed = 3000
            self.input_motorT_speed_T.setText('3000')
        cmd_data = int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_rotator_CCW(self):
        cmd_id = [0x40, 0x71]
        speed = -abs(int(self.input_motorT_speed_T.text()))
        if speed < -3000:
            speed = -3000
            self.input_motorT_speed_T.setText('-3000')
        cmd_data = int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_liftor_up(self):
        cmd_id = [0x40, 0x80]
        speed = abs(int(self.input_motorU_speed_T.text()))
        if speed > 500:
            speed = 500
            self.input_motorU_speed_T.setText('500')
        cmd_data = int32Tolist(-speed * 17895)  #反转
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_liftor_down(self):
        cmd_id = [0x40, 0x80]
        speed = -abs(int(self.input_motorU_speed_T.text()))
        if speed < -500:
            speed = -500
            self.input_motorU_speed_T.setText('-500')
        cmd_data = int32Tolist(-speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorT_mode(self):
        cmd_id = [0x40, 0x12]
        mode = int(self.com_motorT_mode.currentText().split(':')[0])
        if mode < 0: mode = 0x100 + mode
        cmd_data = [0x05, mode]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorU_mode(self):
        cmd_id = [0x40, 0x12]
        mode = int(self.com_motorU_mode.currentText().split(':')[0])
        if mode < 0: mode = 0x100 + mode
        cmd_data = [0x04, mode]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorT_setOrigin(self):  #原点标定
        cmd_id = [0x40, 0x15]
        cmd_data = [0x05]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorU_setOrigin(self):
        cmd_id = [0x40, 0x15]
        cmd_data = [0x04]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorT_position_T(self):
        cmd_id = [0x40, 0x13]
        speed = int(self.input_motorT_speed_T.text())
        if speed > 3000:
            speed = 3000
            self.input_motorT_speed_T.setText('3000')
        elif speed < -3000:
            speed = -3000
            self.input_motorT_speed_T.setText('-3000')
        cmd_data = [0x05] + int32Tolist(int(self.input_motorT_position_T.text())) + int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorU_position_T(self):
        cmd_id = [0x40, 0x13]
        speed = int(self.input_motorU_speed_T.text())
        if speed > 500:
            speed = 500
            self.input_motorU_speed_T.setText('500')
        elif speed < -500:
            speed = -500
            self.input_motorU_speed_T.setText('-500')
        cmd_data = [0x04] + int32Tolist(int(self.input_motorU_position_T.text())) + int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorT_angle_T(self):
        cmd_id = [0x40, 0x17]
        speed = int(self.input_motorT_speed_T.text())
        if speed > 3000:
            speed = 3000
            self.input_motorT_speed_T.setText('3000')
        elif speed < -3000:
            speed = -3000
            self.input_motorT_speed_T.setText('-3000')
        cmd_data = int32Tolist(int(self.input_motorT_angle_T.text())) + int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorU_height_T(self):
        cmd_id = [0x40, 0x18]
        speed = int(self.input_motorU_speed_T.text())
        if speed > 3000:
            speed = 3000
            self.input_motorU_speed_T.setText('500')
        elif speed < -3000:
            speed = -3000
            self.input_motorU_speed_T.setText('-500')
        cmd_data = int32Tolist(int(self.input_motorU_height_T.text())) + int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_liftor_upDown(self, state):
        cmd_id = [0x40, 0x19]
        if state: cmd_data = [0x01]
        else: cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motor_disconnect(self):
        cmd_id = [0x40, 0x14]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motor_communicate(self):
        cmd_id = [0x40, 0x14]
        if self.check_motor_communicate.isChecked():
            cmd_data = [0x01]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_rotator_RR(self):  #转盘同AGV同步旋转
        cmd_id = [0x40, 0x16]
        if self.check_rotator_RR.isChecked():
            cmd_data = [0x01]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 运动轨迹
    def fuc_clear_points(self):
        # self.paint.dotdata=[]
        # self.paint.scatter.setData(self.paint.dotdata)
        self.paint.dotdata
        self.thread_position.clear()
        # self.keyDots=[]

    def fuc_saveTrajectory(self, text):
        print(self.paint.dotdata)
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/trajectory")
        with open(filename, "w", encoding='utf-8') as f:
            json.dump(self.paint.dotdata, f)

    def fuc_loadTrajectory(self, text):
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/trajectory")
        if filename:
            with open(filename, "r", encoding='utf-8') as f:
                content = f.read()
            self.paint.dotdata = json.loads(content)
            self.paint.scatter.setData(self.paint.dotdata)
        else:
            self.fuc_log('取消加载')

    def fuc_playTrajectory(self, direction):
        self.direction_playTrajectory = direction
        self.dotdata = []
        self.paint.scatter.setData(self.dotdata)  #清空点
        self.points_number = len(self.paint.dotdata)  #轨迹中点的数量
        if direction:
            self.point_count = 0  #从第0个点开始
        else:
            self.point_count = self.points_number - 1  #从最后一个点开始
        self.timer9_playPoints.start(300)  #每300ms打印1个点

    def task_playPoints(self):
        if self.direction_playTrajectory:
            if self.point_count < self.points_number:
                self.dotdata.append(self.paint.dotdata[self.point_count])
                self.paint.scatter.setData(self.dotdata)
                self.point_count += 1
            else:
                self.timer9_playPoints.stop()
        else:
            if self.point_count >= 0:
                self.dotdata.append(self.paint.dotdata[self.point_count])
                self.paint.scatter.setData(self.dotdata)
                self.point_count -= 1
            else:
                self.timer9_playPoints.stop()

    def add_keyDot(self, lineSpeed, rateSpeed):
        PosX = float(self.input_agv_currentPosX.text())
        PosY = float(self.input_agv_currentPosY.text())

        #新增红色关键点
        # old_value ={'pos': (PosX, PosY)}
        # new_value ={'pos': (PosX, PosY),'brush': 'r','pen':'r','data':[lineSpeed,rateSpeed]}
        # self.paint.dotdata = [new_value if x == old_value else x for x in self.paint.dotdata]
        # self.paint.scatter.setData(self.paint.dotdata)

        # self.keyDots.append(new_value)

    def fuc_save_keyDot(self, text):
        print(self.keyDots)
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/trajectory")
        with open(filename, "w", encoding='utf-8') as f:
            json.dump(self.keyDots, f)

    def fuc_load_keyDot(self, text):
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/trajectory")
        with open(filename, "r", encoding='utf-8') as f:
            content = f.read()
        self.keyDots = json.loads(content)
        self.paint.scatter.setData(self.keyDots)

    #endregion
    #region # 多点路径规划
    def fuc_pointCycle(self):
        cmd_id = [0x40, 0x52]
        map = int(self.com_map.currentText())
        route = int(self.com_route.currentText())
        startPoint = int(self.input_startPoint.text())
        if (self.check_pointCycle.isChecked()):
            cmd_data = [0x01, map, route, startPoint]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_startPointRecycle(self):
        cmd_id = [0x40, 0x36]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 轨迹跟踪参数
    def fuc_trace_query(self):
        cmd_id = [0x40, 0x40]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_trace_set(self):
        cmd_id = [0x40, 0x40]
        K_Cross = float(self.lineEdit_K_Cross.text())
        K_LowVelo = float(self.lineEdit_K_LowVelo.text())
        K_trace_angle = float(self.lineEdit_K_trace_angle.text())
        K_cross_angle = float(self.lineEdit_K_cross_angle.text())
        cmd_data = [0x01] + floatToList(K_Cross) + floatToList(K_LowVelo) + floatToList(K_trace_angle) + floatToList(K_cross_angle)
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # AGV参数配置
    def fuc_system_sleep(self):
        cmd_id = [0x00, 0x01]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_system_reset(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_software_reset(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x03, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_system_restart(self):
        cmd_id = [0x10, 0x01]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_eeprom_init(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x01, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_activeChip(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x02, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_rtc_init(self):
        cmd_id = [0x10, 0x00]
        # 获取当前日期和时间
        now = datetime.now()
        year = now.year - 2000
        # year = now.year-1999
        month = now.month
        day = now.day
        hour = now.hour
        minute = now.minute
        second = now.second
        msecond = now.microsecond // 1000
        datetime_array = [year, month, day, hour, minute, second, msecond]
        cmd_data = [0x03, year, month, day, hour, minute, second, msecond // 256, msecond % 256]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_save_flashParameter(self):
        cmd_id = [0x10, 0x05]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_read_flashParameter(self):
        cmd_id = [0x10, 0x02]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)
        self.btn_read_flashParameter.setText("读取中……")

    def fuc_set_flashParameter(self):
        cmd_id = [0x10, 0x03]
        cmd_data = []
        for i in range(len(self.agv_parameter)):
            found_widget = self.widget_agvParameter.findChild(QLineEdit, self.agv_parameter[i])  #根据字符串查找widget中的对象
            if i == 0:  #第一个参数为版本号，是整数不是小数
                cmd_data += [int(found_widget.text()), 0, 0, 0]
            else:
                if found_widget.text() == '': found_widget.setText('0')
                cmd_data += floatToList(float(found_widget.text()))

        # scale_rateToLine     =floatToList(float(self.input_scale_rateToLine     .text()))
        # agv_default_lineSpeed=floatToList(float(self.input_agv_default_lineSpeed.text()))
        # agv_default_rateSpeed=floatToList(float(self.input_agv_default_rateSpeed.text()))
        # agv_acceleration     =floatToList(float(self.input_agv_acceleration     .text()))
        # agv_deceleration     =floatToList(float(self.input_agv_deceleration     .text()))
        # agv_RateAcceleration =floatToList(float(self.input_agv_RateAcceleration .text()))
        # agv_RateDeceleration =floatToList(float(self.input_agv_RateDeceleration .text()))
        # cmd_data=scale_rateToLine+agv_default_lineSpeed+agv_default_rateSpeed+agv_acceleration+agv_deceleration+agv_RateAcceleration+agv_RateDeceleration+[0x01]
        # self.fuc_serial_send(cmd_id,cmd_data)
        # time.sleep(0.1)
        # imu_integralGain     =floatToList(float(self.input_imu_integralGain     .text()))
        # imu_GyroOffset_Z=floatToList(float(self.input_imu_offsetZ.text()))
        # agv_angleRangeOffset=floatToList(float(self.input_angleRangeOffset.text()))
        # agv_distanceRangeOffset=floatToList(float(self.input_distanceRangeOffset.text()))
        # factor_rectify=floatToList(float(self.input_factor_rectify.text()))
        # rectifyAngle=floatToList(float(self.input_rectifyAngle.text()))
        # cmd_data=imu_integralGain+imu_GyroOffset_Z+agv_angleRangeOffset+agv_distanceRangeOffset+factor_rectify+rectifyAngle+agv_RateDeceleration+[0x02]
        # self.fuc_serial_send(cmd_id,cmd_data)
        # time.sleep(0.1)
        # LW_compensation=floatToList(float(self.input_LW_compensation.text()))
        # RW_compensation=floatToList(float(self.input_RW_compensation.text()))
        # LW_offset=floatToList(float(self.input_LW_offset.text()))
        # RW_offset=floatToList(float(self.input_RW_offset.text()))
        # cmd_data=LW_compensation+RW_compensation+LW_offset+RW_offset+factor_rectify+rectifyAngle+agv_RateDeceleration+[0x03]
        print(len(cmd_data))
        if (cmd_data[0] != 101): self.fuc_log("请先读取参数！")
        else:
            self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_save_parameter(self):
        # scale_rateToLine       =self.input_scale_rateToLine     .text()
        # agv_default_lineSpeed  =self.input_agv_default_lineSpeed.text()
        # agv_default_rateSpeed  =self.input_agv_default_rateSpeed.text()
        # agv_acceleration       =self.input_agv_acceleration     .text()
        # agv_deceleration       =self.input_agv_deceleration     .text()
        # agv_RateAcceleration   =self.input_agv_RateAcceleration .text()
        # agv_RateDeceleration   =self.input_agv_RateDeceleration .text()
        # imu_integralGain       =self.input_imu_integralGain     .text()
        # imu_GyroOffset_Z       =self.input_imu_offsetZ           .text()
        # agv_angleRangeOffset   =self.input_angleRangeOffset     .text()
        # agv_distanceRangeOffset=self.input_distanceRangeOffset  .text()
        # factor_rectify         =self.input_factor_rectify       .text()
        # rectifyAngle           =self.input_rectifyAngle         .text()
        # LW_compensation        =self.input_LW_compensation      .text()
        # RW_compensation        =self.input_RW_compensation      .text()
        # LW_offset              =self.input_LW_offset            .text()
        # RW_offset              =self.input_RW_offset            .text()
        # text="scale_rateToLine       :"+scale_rateToLine       +"\n"\
        #     +"agv_default_lineSpeed  :"+agv_default_lineSpeed  +"\n"\
        #     +"agv_default_rateSpeed  :"+agv_default_rateSpeed  +"\n"\
        #     +"agv_acceleration       :"+agv_acceleration       +"\n"\
        #     +"agv_deceleration       :"+agv_deceleration       +"\n"\
        #     +"agv_RateAcceleration   :"+agv_RateAcceleration   +"\n"\
        #     +"agv_RateDeceleration   :"+agv_RateDeceleration   +"\n"\
        #     +"imu_integralGain       :"+imu_integralGain       +"\n"\
        #     +"imu_GyroOffset_Z       :"+imu_GyroOffset_Z       +"\n"\
        #     +"agv_angleRangeOffset   :"+agv_angleRangeOffset   +"\n"\
        #     +"agv_distanceRangeOffset:"+agv_distanceRangeOffset+"\n"\
        #     +"factor_rectify         :"+factor_rectify         +"\n"\
        #     +"rectifyAngle           :"+rectifyAngle           +"\n"\
        #     +"LW_compensation        :"+LW_compensation        +"\n"\
        #     +"RW_compensation        :"+RW_compensation        +"\n"\
        #     +"LW_offset              :"+LW_offset              +"\n"\
        #     +"RW_offset              :"+RW_offset
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/parameter")
        print("hlly")
        if filename:
            with open(filename, "w", encoding='utf-8') as f:  #"w"覆盖写入 "a+"追加写入
                # f.write("{}\n".format(text))
                found_widget = self.widget_agvParameter.findChildren(eval('QLineEdit'))  #找出所有QLineEdit对象
                for i in range(len(found_widget)):
                    f.write(found_widget[i].objectName() + ":" + found_widget[i].text() + "\n")
                f.close()
            self.fuc_log("保存成功")

    def fuc_load_parameter(self):
        print("hlly哈利路亚")
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/parameter")
        with open(filename, 'r') as f:
            lines = f.readlines()
            for line in lines:
                if '#' in line:
                    line = line[:line.index('#')]  #去除每行#后的内容
                if line.strip():  #去除字符串两端的空白字符（包括空格、换行符和制表符）
                    parameter = line.strip().split(":", 1)
                    search_string = parameter[0]
                    found_widget = self.widget_agvParameter.findChild(QLineEdit, search_string)  #根据字符串查找widget中的对象
                    if found_widget:
                        found_widget.setText(parameter[1])
            # lines = f.readlines()
            # # for line in lines:
            # #     line_content = line.rstrip("\n").split(":")
            # #     print(line_content[1])
            # self.input_scale_rateToLine     .setText(lines[0].rstrip("\n").split(":")[1])
            # self.input_agv_default_lineSpeed.setText(lines[1].rstrip("\n").split(":")[1])
            # self.input_agv_default_rateSpeed.setText(lines[2].rstrip("\n").split(":")[1])
            # self.input_agv_acceleration     .setText(lines[3].rstrip("\n").split(":")[1])
            # self.input_agv_deceleration     .setText(lines[4].rstrip("\n").split(":")[1])
            # self.input_agv_RateAcceleration .setText(lines[5].rstrip("\n").split(":")[1])
            # self.input_agv_RateDeceleration .setText(lines[6].rstrip("\n").split(":")[1])
            # self.input_imu_integralGain     .setText(lines[7].rstrip("\n").split(":")[1])
            # self.input_imu_offsetZ           .setText(lines[8].rstrip("\n").split(":")[1])
            # self.input_angleRangeOffset     .setText(lines[9].rstrip("\n").split(":")[1])
            # self.input_distanceRangeOffset  .setText(lines[10].rstrip("\n").split(":")[1])
            # self.input_factor_rectify       .setText(lines[11].rstrip("\n").split(":")[1])
            # self.input_rectifyAngle         .setText(lines[12].rstrip("\n").split(":")[1])
            # self.input_LW_compensation      .setText(lines[13].rstrip("\n").split(":")[1])
            # self.input_RW_compensation      .setText(lines[14].rstrip("\n").split(":")[1])
            # self.input_LW_offset            .setText(lines[15].rstrip("\n").split(":")[1])
            # self.input_RW_offset            .setText(lines[16].rstrip("\n").split(":")[1])

    #endregion
    #region # AGV程序升级
    def fuc_iap_in(self):  #进入bootloader
        cmd_id = [0x00, 0x00]
        cmd_data = [0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_app_in(self):  #进入APP（取消升级）
        cmd_id = [0x00, 0x00]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_iap_sendLen(self):  #发送固件长度
        #选择固件
        self.iap_filename, _ = QFileDialog.getOpenFileName(None, 'Open File', self.input_app_address.text())
        if self.iap_filename:
            with open(self.iap_filename, 'rb') as f:
                data = f.read()

            #发送固件长度
            self.fileLen = len(data)
            self.fuc_log("程序大小为%d个字节" % self.fileLen)
            self.pgB_upgrade.setValue(0)  #progressBar
            self.pgB_upgrade.setMaximum(self.fileLen)
            for i in range(len(self.servers_pgBars)):
                self.servers_pgBars[i].setMaximum(self.fileLen)
                # self.servers_pgBars[i].setRange(0, self.fileLen)
                self.servers_pgBars[i].setValue(0)

            lendata = [len(data) >> 24, len(data) >> 16 & 0xff, len(data) >> 8 & 0xff, len(data) & 0xff]
            cmd_id = [0x00, 0x00]
            cmd_data = [0x03] + lendata
            self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_receive_in(self):  #进入接收固件状态
        cmd_id = [0x00, 0x00]
        cmd_data = [0x02]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_uart_sendBin(self, sendData2):  #分包发送时回调
        global global_s
        if self.widget_connect.check_mode_wifiToUart.isChecked():
            global_s.send(sendData2)
        elif self.rxflag:
            self.ser.write(sendData2)
        elif self.flag_server:
            self.server_thread.signal_send_data.emit(sendData2)
        # self.fuc_log('发送完成')
        # self.thread_sendBin.join()
    def fuc_iap_sendBin(self):  #发送bin文件
        global global_s
        if self.check_sendBin_pack.isChecked():  #分包发送
            self.thread_sendBin = thread_sendBin(self.input_app_address.text())
            self.thread_sendBin.sig_sendBin.connect(self.fuc_uart_sendBin)
            self.thread_sendBin.start()
        else:  #直接整个文件发送
            self.iap_filename, _ = QFileDialog.getOpenFileName(None, 'Open File', self.input_app_address.text())
            with open(self.iap_filename, 'rb') as f:
                data = f.read()
            # 发送固件
            sendData2 = bytes(data)
            if self.widget_connect.check_mode_wifiToUart.isChecked():
                global_s.send(sendData2)
            elif self.rxflag:
                self.ser.write(sendData2)
            elif self.flag_server:
                self.server_thread.signal_send_data.emit(sendData2)
            for i in range(len(self.servers_thread)):
                self.servers_thread[i].send_message(sendData2)
            self.fuc_log('发送完成')

    def fuc_updateProgram(self):  #一键升级
        #进入IAP(单片机作为服务端时不能自动，需手动重连)
        # self.fuc_iap_in()
        # time.sleep(0.3)

        #选择固件
        self.iap_filename, _ = QFileDialog.getOpenFileName(None, 'Open File', self.input_app_address.text())
        with open(self.iap_filename, 'rb') as f:
            data = f.read()

        #发送固件长度
        self.fileLen = len(data)
        self.pgB_upgrade.setValue(0)  #progressBar
        self.pgB_upgrade.setMaximum(self.fileLen)
        self.fuc_log("程序大小为%d个字节" % self.fileLen)
        lendata = [len(data) >> 24, len(data) >> 16 & 0xff, len(data) >> 8 & 0xff, len(data) & 0xff]
        cmd_id = [0x00, 0x00]
        cmd_data = [0x03] + lendata
        self.fuc_serial_send(cmd_id, cmd_data)
        time.sleep(0.3)

        #进入接收固件状态
        cmd_id = [0x00, 0x00]
        cmd_data = [0x02]
        self.fuc_serial_send(cmd_id, cmd_data)
        self.fuc_log('进入接收固件状态')
        time.sleep(0.3)

        #发送固件
        # data=data[0:self.packLen]
        sendData2 = bytes(data)
        global global_s
        if self.widget_connect.check_mode_wifiToUart.isChecked():
            global_s.send(sendData2)
        elif self.rxflag:
            self.ser.write(sendData2)
        elif self.flag_server:
            self.server_thread.signal_send_data.emit(sendData2)
        self.fuc_log('发送完成')

        #进入APP(需等待接收完成；单片机作为服务端时不能自动，需手动重连)
        # self.fuc_app_in()
        # time.sleep(0.3)

    #endregion
    #region # 校准IMU（陀螺仪）
    def fuc_imu_test(self, cw):
        if (self.flag_imu_test):
            self.flag_imu_test = 0
            self.timer10_imuTest.stop()
        else:
            self.flag_imu_test = 1
            self.angle_count = 1
            self.imu_rotateCount = 0
            if cw == 1:
                self.angleList = [0, 90, 180, -90]
            elif cw == 0:
                self.angleList = [0, -90, -180, 90]
            elif cw == 2:
                self.angleList = [0, 90, 0, -90]
            self.task_imuTest()
            self.timer10_imuTest.start(5000)

    def task_imuTest(self):
        self.input_agv_targetAngle.setText(str(self.angleList[self.angle_count]))
        self.fuc_agv_toAngle()
        self.angle_count += 1
        if (self.angle_count > 3): self.angle_count = 0

        self.imu_rotateCount += 1
        self.input_imu_rotateCount.setText(str(self.imu_rotateCount))

    def fuc_set_rateSpeed(self, value):
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = value / 1000
        self.widget_imu.input_rateSpeed.setText(str(rateSpeed))
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_set1_rateSpeed(self):
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = float(self.widget_imu.input_rateSpeed.text())
        maxRateSpeed = 0.7
        if rateSpeed > maxRateSpeed:
            rateSpeed = maxRateSpeed
            self.widget_imu.input_rateSpeed.setText(str(maxRateSpeed))
        elif rateSpeed < -maxRateSpeed:
            rateSpeed = -maxRateSpeed
            self.widget_imu.input_rateSpeed.setText(str(-maxRateSpeed))
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_reset_rateSpeed(self):
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = 0
        self.widget_imu.slider_rateSpeed.valueChanged.disconnect(self.fuc_set_rateSpeed)  #先断开滑动条绑定的槽函数，防止input_rateSpeed也被复位为0
        self.widget_imu.slider_rateSpeed.setValue(0)
        self.widget_imu.slider_rateSpeed.valueChanged.connect(self.fuc_set_rateSpeed)
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_reset_curve(self):
        self.thread_angle.y = [0] * 6
        self.thread_angle.dataNum_current = 0

    def fuc_offset_calibration(self):
        gyroX = self.widget_imu.input_average_gyroX.text()
        gyroY = self.widget_imu.input_average_gyroY.text()
        gyroZ = self.widget_imu.input_average_gyroZ.text()
        self.widget_imu.input_gyroX.setText(gyroX)
        self.widget_imu.input_gyroY.setText(gyroY)
        self.widget_imu.input_gyroZ.setText(gyroZ)
        l_gyroX = floatToList(float(gyroX))
        l_gyroY = floatToList(float(gyroY))
        l_gyroZ = floatToList(float(gyroZ))
        cmd_id = [0x40, 0x23]
        cmd_data = l_gyroX + l_gyroY + l_gyroZ
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 地图二维码
    def fuc_load_map_DmCode(self):
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/map_DmCode", "Excel Files (*.xlsx);;All Files (*)")
        workbook = openpyxl.load_workbook(filename)
        sheet = workbook.active
        for row in range(2, sheet.max_row + 1):
            self.paint.dotdata.append({
                'pos': (sheet.cell(row, 2).value, sheet.cell(row, 3).value),
                'brush': pg.mkBrush('b'),
                'pen': 'b',
                'data': sheet.cell(row, 1).value
            })
        # print(self.paint.dotdata)
        self.paint.scatter.setData(self.paint.dotdata)

    #endregion
    #region # 二维码标定
    def fuc_page_next(self):
        if (self.widget_tags.stack_tags.currentIndex() == self.widget_tags.stack_tags.count() - 1): return
        id = self.widget_tags.stack_tags.currentIndex() + 1
        self.widget_tags.stack_tags.setCurrentIndex(id)
        self.widget_tags.input_page_current.setText(str(id + 1))

    def fuc_page_pre(self):
        if (self.widget_tags.stack_tags.currentIndex() == 0): return
        id = self.widget_tags.stack_tags.currentIndex() - 1
        self.widget_tags.stack_tags.setCurrentIndex(id)
        self.widget_tags.input_page_current.setText(str(id + 1))

    def fuc_page_jump(self):
        id = int(self.widget_tags.input_page_current.text()) - 1
        print(id)
        self.widget_tags.stack_tags.setCurrentIndex(id)

    def fuc_clear_Dmcode(self):
        print("hlly")
        self.DMcode_number = 0
        # 删除所有页面
        while self.widget_tags.stack_tags.count():
            self.widget_tags.stack_tags.removeWidget(self.widget_tags.stack_tags.currentWidget())
        self.line_edits = []
        self.checkBoxs = []
        self.dmcode_labels = []

    def fuc_query_DmCode(self):
        self.fuc_clear_Dmcode()
        cmd_id = [0x40, 0x50]
        startAddress = int(self.widget_tags.input_start_address.text())
        num = int(self.widget_tags.input_tags_number.text())
        cmd_data = int16Tolist_big(startAddress) + int16Tolist_big(num)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_check_DmCode(self):
        if self.widget_tags.check_all.isChecked():
            for i in range(len(self.checkBoxs)):
                self.checkBoxs[i].setChecked(True)
        else:
            for i in range(len(self.checkBoxs)):
                self.checkBoxs[i].setChecked(False)

    def fuc_upload_DmCode(self):
        try:
            self.DMcode_number = int(len(self.line_edits) / 4)
            self.widget_tags.pgB_upload.setMaximum(self.DMcode_number - 1)
            for i in range(self.DMcode_number):
                if self.checkBoxs[i].isChecked():
                    # print(i)

                    DM_tagNumber = int(self.line_edits[4 * i].text())
                    DM_posX = float(self.line_edits[4 * i + 1].text())
                    DM_posY = float(self.line_edits[4 * i + 2].text())
                    DM_ErrorAngle = float(self.line_edits[4 * i + 3].text())
                    # i+=256
                    self.dmcode_labels[i].text()
                    DM_codeNum = int16Tolist_big(int(self.dmcode_labels[i].text()))
                    # print(DM_tagNumber)
                    # print(DM_posX)
                    # print(DM_posY)
                    # print(DM_ErrorAngle)
                    cmd_id = [0x40, 0x51]
                    cmd_data = int32Tolist(DM_tagNumber) + floatToList(DM_posX) + floatToList(DM_posY) + floatToList(DM_ErrorAngle) + DM_codeNum
                    self.fuc_serial_send(cmd_id, cmd_data)
                    sendInterval = int(self.widget_tags.input_sendInterval.text()) / 1000
                    # print(sendInterval)#发送时间间隔
                    time.sleep(sendInterval)
                # self.widget_tags.pgB_upload.setValue(i)
                self.signal.pgB_DmCode.emit(i)
        except:
            self.widget_tags.info.setText(get_time() + " " + '请先打开文件或者查询！')
            self.widget_tags.info.setStyleSheet("color: red;")

    def fuc_set_DmCode(self):
        thread_upload_DmCode = threading.Thread(target=self.fuc_upload_DmCode)
        thread_upload_DmCode.start()

    def fuc_save_DmCode(self):
        # 获取用户选择的文件路径和名称
        try:
            file_name, _ = QFileDialog.getSaveFileName(self, "Save File", parent_dir_path + "/appDoc/DmCode", "Excel Files (*.xlsx);;All Files (*)")
        except:
            wb = openpyxl.Workbook()
            wb.save(f"{file_name}.xlsx")
        if not file_name:
            return
        # 创建Workbook对象并写入数据
        try:
            wb = openpyxl.load_workbook(file_name)
        except:
            wb = openpyxl.Workbook()
        ws = wb.active
        title = ['序号', 'tag值', 'X(mm)', 'Y(mm)', '误差角度(°)']
        for col in range(5):
            cell_value = title[col]
            ws.cell(row=1, column=col + 1, value=cell_value)
        try:
            for row in range(self.DMcode_number):
                cell_value = int(self.dmcode_labels[row].text())
                ws.cell(row=row + 2, column=1, value=cell_value)
                for col in range(4):
                    if self.line_edits[4 * row + col].text() != 'None':
                        cell_value = float(self.line_edits[4 * row + col].text())
                        ws.cell(row=row + 2, column=col + 2, value=cell_value)
        except:
            self.widget_tags.info.setText(get_time() + " " + '保存数据为空！')
        # 保存文件
        try:
            wb.save(file_name)
            self.widget_tags.info.setText(get_time() + " " + "保存成功！")
        except Exception as e:
            self.widget_tags.info.setText(get_time() + " " + str(e))
            self.widget_tags.info.setStyleSheet("color: red;")

    def fuc_load_DmCode(self):
        print("hlly")
        self.fuc_clear_Dmcode()
        # 打开Excel文件
        # filename=parent_dir_path+"/appDoc/DmCode/abc.xlsx"
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/DmCode", "Excel Files (*.xlsx);;All Files (*)")
        workbook = openpyxl.load_workbook(filename)

        # 选择工作表
        sheet = workbook.active

        # 遍历所有行和列，读取单元格数据
        for row in range(1, sheet.max_row):
            row -= 1
            # 创建新页面
            if row % self.page_codeNum == 0:
                self.new_page = QWidget()
                self.grid_layout = QGridLayout()
                self.new_page.setLayout(self.grid_layout)
                self.widget_tags.stack_tags.addWidget(self.new_page)
                # labelList=["","序号","二维码编码","坐标X","坐标Y","补偿角度"]
                labelList = ["", "序号", "二维码编码", "坐标X", "坐标Y", "补偿角度"]
                for i in range(3):
                    for j in range(6):
                        label = QLabel(labelList[j])
                        self.grid_layout.addWidget(label, 0, 6 * i + j)

            # 创建输入框
            check = QCheckBox()
            check.setChecked(True)
            self.checkBoxs.append(check)
            label = QLabel(str(sheet.cell(row + 2, 1).value))
            self.dmcode_labels.append(label)
            self.grid_layout.addWidget(check, row % self.page_codeNum // 3 + 1, 6 * (row % self.page_codeNum % 3))
            self.grid_layout.addWidget(label, row % self.page_codeNum // 3 + 1, 6 * (row % self.page_codeNum % 3) + 1)
            for j in range(4):
                line_edit = QLineEdit(self)
                line_edit.setObjectName(f'line_edit_{4*row+j}')
                self.line_edits.append(line_edit)
                self.grid_layout.addWidget(line_edit, row % self.page_codeNum // 3 + 1, 6 * (row % self.page_codeNum % 3) + j + 2)
                self.line_edits[4 * row + j].setText(str(sheet.cell(row + 2, j + 2).value))
            self.DMcode_number += 1
        # 关闭Excel文件
        workbook.close()
        self.widget_tags.info.setText(get_time() + " " + "加载完成！")

    #endregion
    #region # 调度模拟
    def fuc_cmdNumChange(self, index):  #命令号切换联动命令名切换
        widget_index = self.layout_cmd.indexOf(self.sender())  #self.layout_cmd.layout().indexOf(self.sender())
        widget = self.layout_cmd.itemAt(widget_index + 10).widget()  #10条指令，对应后一列name
        widget.setCurrentIndex(index)

    def fuc_cmdNameChange(self, index):  #命令名切换联动命令号切换
        widget_index = self.layout_cmd.indexOf(self.sender())  #self.layout_cmd.layout().indexOf(self.sender())
        widget = self.layout_cmd.itemAt(widget_index - 10).widget()  #10条指令，对应前一列num
        widget.setCurrentIndex(index)

    def fuc_change_cmdNum(self, state):  #命令号自动递增
        if state == 2:  # 2 表示 Qt.Checked
            widget_index = self.layout_cmd.indexOf(self.sender())  #self.layout_cmd.layout().indexOf(self.sender())
            widget = self.layout_cmd.itemAt(widget_index + 10).widget()  #10条指令，对应后一列name
            if widget.text() != "00":
                widget.setText(str(self.cmdNum))
                self.cmdNum += 1
                if self.cmdNum >= 9999:
                    self.cmdNum = 0

    def fuc_check_cmd_all(self):  #选择所有指令
        widget = self.layout_cmd.itemAt(0).widget()  #对应全选框
        if widget.isChecked():
            for i in range(self.num_cmd):
                widget = self.layout_cmd.itemAt(self.cmd_column + i).widget()
                widget.setChecked(True)
        else:
            for i in range(self.num_cmd):
                widget = self.layout_cmd.itemAt(self.cmd_column + i).widget()
                widget.setChecked(False)

    def fuc_cmdSave(self):  #批量保存指令
        #保存为
        # 获取用户选择的文件路径和名称
        try:
            file_name, _ = QFileDialog.getSaveFileName(self, "Save File", parent_dir_path + "/appDoc/cmd", "Excel Files (*.xlsx);;All Files (*)")
        except:
            wb = openpyxl.Workbook()
            wb.save(f"{file_name}.xlsx")
        if not file_name:
            return
        # 创建Workbook对象并写入数据
        try:
            wb = openpyxl.load_workbook(file_name)
        except:
            wb = openpyxl.Workbook()
        ws = wb.active
        title = ['选中', '指令码', '指令名', '参数1-1', '参数1-2', '参数1-3', '参数2-1', '参数2-2', '参数2-3', '参数3-1']
        for col in range(10):
            cell_value = title[col]
            ws.cell(row=1, column=col + 1, value=cell_value)
        num_cmd = self.num_cmd
        for i in range(num_cmd):
            for j in range(10):
                widget = self.layout_cmd.itemAt(self.cmd_column + j * 10 + i).widget()
                if j == 0:
                    cell_value = "True" if widget.isChecked() else "False"
                    ws.cell(row=1 + i + 1, column=1 + j, value=cell_value)
                elif j == 2:
                    cell_value = widget.currentText()
                    ws.cell(row=1 + i + 1, column=1 + j, value=cell_value)
                else:
                    if widget.text() == "":
                        cell_value = "0"
                    else:
                        cell_value = widget.text()
                    ws.cell(row=1 + i + 1, column=1 + j, value=cell_value)
        # 保存文件
        try:
            wb.save(file_name)
            self.widget_tags.info.setText(get_time() + " " + "保存成功！")
        except Exception as e:
            self.widget_tags.info.setText(get_time() + " " + str(e))
            self.widget_tags.info.setStyleSheet("color: red;")
        #保存为txt
        # filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path+"/appDoc/cmd")
        # print(filename)
        # num_cmd=self.num_cmd
        # with open(filename, "w",encoding='utf-8') as f:

        #     for i in range(num_cmd):
        #         for j in range(10):
        #             widget = self.layout_cmd.itemAt(self.cmd_column+j*10+i).widget()
        #             if j==0:
        #                 f.write(("True" if widget.isChecked() else "False")+"  ")
        #             elif j==2 :
        #                 f.write(widget.currentText()+"  ")
        #             else:
        #                 if widget.text()=="":
        #                     f.write("0  ")
        #                 else:f.write(widget.text()+"  ")
        #         f.write("\n")
        self.fuc_log("保存完成")

    def fuc_cmdLoad(self):  #批量加载指令
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/cmd", "Excel Files (*.xlsx);;All Files (*)")
        workbook = openpyxl.load_workbook(filename)
        sheet = workbook.active
        for row in range(2, sheet.max_row + 1):
            # print(row)
            cellvalue = str(sheet.cell(row, 1).value)
            print(cellvalue)
            for i in range(10):
                widget = self.layout_cmd.itemAt(self.cmd_column + i * 10 + row - 2).widget()
                if i == 0: widget.setChecked(True if str(sheet.cell(row, i + 1).value) == "True" else False)
                elif i == 2: widget.setCurrentText(str(sheet.cell(row, i + 1).value))
                else:
                    try:
                        widget.setText(str(sheet.cell(row, i + 1).value))
                    except:
                        widget.setText('')
        workbook.close()
        # 从txt文件中读取数据
        # filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc//cmd")
        # print(filename)
        # with open(filename, "r", encoding='utf-8') as f:
        #     line_number = 0
        #     for line in f.readlines():
        #         line = line.strip().split("  ")
        #         # print(line)
        #         for i in range(10):
        #             widget = self.layout_cmd.itemAt(self.cmd_column + i * 10 + line_number).widget()
        #             if i == 0: widget.setChecked(True if line[i] == "True" else False)
        #             elif i == 2: widget.setCurrentText(line[i])
        #             else:
        #                 try:
        #                     widget.setText(line[i])
        #                 except:
        #                     widget.setText('')
        #         line_number += 1
        self.fuc_log("加载完成")

    def fuc_sendUiCmd_TCS(self, ID=1, name="GO", p1=0, p2=0, p3=0, p4=0, p5=0, p6=0, p7=0):
        num_list = [ID >> 8 & 0xff, ID & 0xff]
        name_list = [name.encode('utf-8')[0], name.encode('utf-8')[1]]
        tem_cmd = num_list + name_list
        p = [p1, p2, p3, p4, p5, p6, p7]
        p_list = [0, 0, 0, 0, 0, 0, 0]
        for j in range(7):
            p_list[j] = [p[j] >> 24, p[j] >> 16 & 0xff, p[j] >> 8 & 0xff, p[j] & 0xff]
            tem_cmd += p_list[j]
        self.fuc_sendData_TCS(tem_cmd)

    def fuc_sendCmd_TCS(self):
        S_cmd = []
        num_cmd = self.num_cmd
        for i in range(num_cmd):
            widget1 = self.layout_cmd.itemAt(self.cmd_column + i).widget()
            widget = self.layout_cmd.itemAt(self.cmd_column + (1) * num_cmd + i).widget()
            if (widget1.isChecked()):
                num = int(widget.text())
            else:
                num = 0
            num_list = [num >> 8 & 0xff, num & 0xff]
            widget = self.layout_cmd.itemAt(self.cmd_column + (2) * num_cmd + i).widget()
            name = widget.currentText()
            name_list = [name.encode('utf-8')[0], name.encode('utf-8')[1]]
            tem_cmd = num_list + name_list
            p = [0, 0, 0, 0, 0, 0, 0]
            p_list = [0, 0, 0, 0, 0, 0, 0]
            for j in range(7):
                widget = self.layout_cmd.itemAt(self.cmd_column + (3 + j) * num_cmd + i).widget()
                if (widget.text() == ""): p[j] = 0
                else: p[j] = int(widget.text())
                if (p[j] < 0): p[j] = 0x100000000 + p[j]
                # p[j]_list=[p[j]>>24,p[j]>>16&0xff,p[j]>>8&0xff,p[j]&0xff]# int32_t 单片机内存存储时高位在后
                # p_list[j]=[p[j]&0xff,p[j]>>8&0xff,p[j]>>16&0xff,p[j]>>24]
                p_list[j] = [p[j] >> 24, p[j] >> 16 & 0xff, p[j] >> 8 & 0xff, p[j] & 0xff]
                tem_cmd += p_list[j]
            S_cmd += tem_cmd
            # print(S_cmd)
        # self.sendData_to_clients(S_cmd)
        self.fuc_sendData_TCS(S_cmd)

    def fuc_sendData_TCS(self, S_cmd):  #向服务器（控制板）发送数据
        Block_counter = '0001'
        length = str(len(S_cmd) + 24).zfill(5)  #报文尾长度不计算在内
        # print(length)
        type = 'N'
        source = 'R'
        target = 'S'
        Distribution_code = 'ALFF'
        identifier = 'FP'
        Function_point = '003631'
        Telegram = Block_counter + length + type + source + target + Distribution_code + identifier + Function_point
        byte_Telegram = Telegram.encode()
        byte_Telegram += bytes(S_cmd)
        # print(byte_Telegram)
        byte_Telegram += bytes([0])
        global global_s
        if self.widget_connect.check_mode_wifiToUart.isChecked():
            global_s.send(byte_Telegram)
        if (self.widget_log.check_showsSendMessage.isChecked()):  #日志打印发送数据
            self.fuc_log("Tx:" + "  " + HexStrAddSpace(byte_Telegram.hex()))

    def fuc_switchAuthority(self):
        cmd_id = [0x80, 0x01]
        if self.check_switchAuthority.isChecked():
            cmd_data = [0x02]
            self.fuc_log('切换为调度控制')
        else:
            cmd_data = [0x01]
            self.fuc_log('切换为触摸屏控制')
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 更新摄像头画面
    def update_DM(self, number, x, y, h, w):
        # print(number)
        self.input_self.DMcode_number.setText(number)
        self.input_dm_x.setText(str(x))
        self.input_dm_y.setText(str(y))
        self.input_dm_h.setText(str(h))
        self.input_dm_w.setText(str(w))
        angle = math.atan2(h, w) / math.pi * 180
        angle = round(angle, 2)
        angle += 45
        if (angle > 180): angle = angle - 360
        elif (angle < -180): angle = angle + 360
        self.input_dm_angle.setText(str(angle))

    def update_image(self, pixmap):
        self.label_video.setPixmap(QPixmap.fromImage(pixmap))
        # # 将QImage转换为QPixmap
        # qpixmap = QPixmap.fromImage(pixmap)
        # # 将QPixmap转换为OpenCV的Mat对象
        # mat = cv2.cvtColor(qpixmap.toImage().copy(), cv2.COLOR_BGR2RGB)
        # cv2.imshow("Image", mat)
        # cv2.waitKey(0)
    def fuc_decode(self):
        if self.flag_camera:
            self.video_thread.fuc_decode()

    def update_frame(self):
        # 读取摄像头图像
        ret, frame = self.cap.read()
        if not ret:  # 如果读取失败，说明摄像头不存在或无画面
            print("摄像头不存在或无画面")

        # 将图像从OpenCV格式转换为QImage格式
        try:
            rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            q_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)

            # 将QImage格式转换为QPixmap格式并显示在标签上
            pixmap = QPixmap.fromImage(q_image)
            self.label_video.setPixmap(pixmap)
        except:
            print("cv2.cvtColor() error!")
            self.timer12.stop()

    #endregion
    #region # 设置窗口大小
    def resizeWindow(self):
        self.resize(100, 100)
        self.timer13.stop()
        size = self.size()
        print("窗口宽度：", size.width())
        print("窗口高度：", size.height())

    # def timer_Test(self):
    #     print("timerTest")
    # def resizeEvent(self, event):
    # size = self.size()
    # print(f"窗口宽度：{size.width()}, 窗口高度：{size.height()}")
    #endregion
    #region # 实现鼠标拖拽功能
    def mousePressEvent(self, event):
        self.pressX = event.x()  #记录鼠标按下的时候的坐标
        self.pressY = event.y()

    def mouseMoveEvent(self, event):
        try:
            x = event.x()
            y = event.y()  #获取移动后的坐标
            moveX = x - self.pressX
            moveY = y - self.pressY  #计算移动了多少

            positionX = self.frameGeometry().x() + moveX
            positionY = self.frameGeometry().y() + moveY  #计算移动后主窗口在桌面的位置
            self.move(positionX, positionY)  #移动主窗口
        except:
            pass

    #endregion
    #region # 麦克纳姆轮小车调试
    def toggle_widget_mecanum(self, checked):
        if checked:
            self.widget_mecanum.show()
        else:
            self.widget_mecanum.hide()

    def mecanum_mode_control(self):  #控制模式切换
        cmd_id = [0x40, 0x51]
        if self.widget_mecanum.check_mode_control.isChecked(): cmd_data = [1]
        else: cmd_data = [0]
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_stop(self):
        cmd_id = [0x40, 0x63]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)
        self.widget_mecanum.S_setPWM_LF.setValue(0)
        self.widget_mecanum.S_setPWM_RF.setValue(0)
        self.widget_mecanum.S_setPWM_LB.setValue(0)
        self.widget_mecanum.S_setPWM_RB.setValue(0)
        self.widget_mecanum.S_setSpeed_LF.setValue(0)
        self.widget_mecanum.S_setSpeed_RF.setValue(0)
        self.widget_mecanum.S_setSpeed_LB.setValue(0)
        self.widget_mecanum.S_setSpeed_RB.setValue(0)

        self.widget_mecanum.S_speedX.setValue(0)
        self.widget_mecanum.S_speedY.setValue(0)
        self.widget_mecanum.S_speedZ.setValue(0)

    def mecanum_setMaxspeed(self):
        maxspeed = int(self.widget_mecanum.input_maxSpeed.text())
        self.widget_mecanum.S_setSpeed_LF.setMaximum(maxspeed)
        self.widget_mecanum.S_setSpeed_RF.setMaximum(maxspeed)
        self.widget_mecanum.S_setSpeed_LB.setMaximum(maxspeed)
        self.widget_mecanum.S_setSpeed_RB.setMaximum(maxspeed)
        self.widget_mecanum.S_setSpeed_LF.setMinimum(-maxspeed)
        self.widget_mecanum.S_setSpeed_RF.setMinimum(-maxspeed)
        self.widget_mecanum.S_setSpeed_LB.setMinimum(-maxspeed)
        self.widget_mecanum.S_setSpeed_RB.setMinimum(-maxspeed)
        # self.widget_mecanum.S_speedX.setMaximum(maxspeed)
        # self.widget_mecanum.S_speedX.setMinimum(-maxspeed)
    def mecanum_setPWM1(self, wheel):  #通过文本输入框设置
        if wheel == 'LF':
            valueLF = int(self.widget_mecanum.input_setPWM_LF.text())
            self.widget_mecanum.S_setPWM_LF.setValue(valueLF)
            pwmLF = int16Tolist(valueLF)
            self.mecanum_pwm[0] = pwmLF[0]
            self.mecanum_pwm[1] = pwmLF[1]
        elif wheel == 'RF':
            valueRF = int(self.widget_mecanum.input_setPWM_RF.text())
            self.widget_mecanum.S_setPWM_RF.setValue(valueRF)
            pwmRF = int16Tolist(valueRF)
            self.mecanum_pwm[2] = pwmRF[0]
            self.mecanum_pwm[3] = pwmRF[1]
        elif wheel == 'LB':
            valueLB = int(self.widget_mecanum.input_setPWM_LB.text())
            self.widget_mecanum.S_setPWM_LB.setValue(valueLB)
            pwmLB = int16Tolist(valueLB)
            self.mecanum_pwm[4] = pwmLB[0]
            self.mecanum_pwm[5] = pwmLB[1]
        elif wheel == 'RB':
            valueRB = int(self.widget_mecanum.input_setPWM_RB.text())
            self.widget_mecanum.S_setPWM_RB.setValue(valueRB)
            pwmRB = int16Tolist(valueRB)
            self.mecanum_pwm[6] = pwmRB[0]
            self.mecanum_pwm[7] = pwmRB[1]

        print('fuc_change_wheel_pwm1')
        cmd_id = [0x40, 0x61]
        cmd_data = self.mecanum_pwm
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setPWM(self):  #通过滑动条设置
        valueLF = self.widget_mecanum.S_setPWM_LF.value()
        valueRF = self.widget_mecanum.S_setPWM_RF.value()
        valueLB = self.widget_mecanum.S_setPWM_LB.value()
        valueRB = self.widget_mecanum.S_setPWM_RB.value()
        # self.widget_mecanum.label_wheel_LF.setText(str(valueLF))
        # self.widget_mecanum.label_wheel_RF.setText(str(valueRF))
        # self.widget_mecanum.label_wheel_LB.setText(str(valueLB))
        # self.widget_mecanum.label_wheel_RB.setText(str(valueRB))
        self.widget_mecanum.input_setPWM_LF.setText(str(valueLF))
        self.widget_mecanum.input_setPWM_RF.setText(str(valueRF))
        self.widget_mecanum.input_setPWM_LB.setText(str(valueLB))
        self.widget_mecanum.input_setPWM_RB.setText(str(valueRB))

        pwmLF = int16Tolist(valueLF)
        pwmRF = int16Tolist(valueRF)
        pwmLB = int16Tolist(valueLB)
        pwmRB = int16Tolist(valueRB)
        cmd_id = [0x40, 0x61]
        cmd_data = pwmLF + pwmRF + pwmLB + pwmRB
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setRPM1(self):  #通过文本输入框设置目标转速
        valueLF = int(self.widget_mecanum.input_setSpeed_LF.text())
        valueRF = int(self.widget_mecanum.input_setSpeed_RF.text())
        valueLB = int(self.widget_mecanum.input_setSpeed_LB.text())
        valueRB = int(self.widget_mecanum.input_setSpeed_RB.text())
        self.widget_mecanum.S_setSpeed_LF.setValue(valueLF)
        self.widget_mecanum.S_setSpeed_RF.setValue(valueRF)
        self.widget_mecanum.S_setSpeed_LB.setValue(valueLB)
        self.widget_mecanum.S_setSpeed_RB.setValue(valueRB)
        speedLF = int16Tolist(valueLF)
        speedRF = int16Tolist(valueRF)
        speedLB = int16Tolist(valueLB)
        speedRB = int16Tolist(valueRB)
        cmd_id = [0x40, 0x62]
        cmd_data = speedLF + speedRF + speedLB + speedRB
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setRPM(self):  #通过滑动条设置目标转速
        valueLF = self.widget_mecanum.S_setSpeed_LF.value()
        valueRF = self.widget_mecanum.S_setSpeed_RF.value()
        valueLB = self.widget_mecanum.S_setSpeed_LB.value()
        valueRB = self.widget_mecanum.S_setSpeed_RB.value()
        self.widget_mecanum.input_setSpeed_LF.setText(str(valueLF))
        self.widget_mecanum.input_setSpeed_RF.setText(str(valueRF))
        self.widget_mecanum.input_setSpeed_LB.setText(str(valueLB))
        self.widget_mecanum.input_setSpeed_RB.setText(str(valueRB))
        speedLF = int16Tolist(valueLF)
        speedRF = int16Tolist(valueRF)
        speedLB = int16Tolist(valueLB)
        speedRB = int16Tolist(valueRB)
        cmd_id = [0x40, 0x62]
        cmd_data = speedLF + speedRF + speedLB + speedRB
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setSpeed(self):  #通过滑动条设置目标速度
        value = self.widget_mecanum.S_speedX.value()
        self.widget_mecanum.input_speedX.setText(str(value))
        if self.widget_mecanum.check_speedX.isChecked():
            self.mecanum_move(value)

    def mecanum_setSpeed1(self):  #通过文本输入框设置目标速度
        value = int(self.widget_mecanum.input_speedX.text())
        self.widget_mecanum.S_speedX.setValue(value)
        if self.widget_mecanum.check_speedX.isChecked():
            self.mecanum_move(value)

    def mecanum_move(self, value):
        self.widget_mecanum.input_setSpeed_LF.setText(str(value))
        self.widget_mecanum.input_setSpeed_RF.setText(str(value))
        self.widget_mecanum.input_setSpeed_LB.setText(str(value))
        self.widget_mecanum.input_setSpeed_RB.setText(str(value))
        self.widget_mecanum.S_setSpeed_LF.setValue(value)
        self.widget_mecanum.S_setSpeed_RF.setValue(value)
        self.widget_mecanum.S_setSpeed_LB.setValue(value)
        self.widget_mecanum.S_setSpeed_RB.setValue(value)
        cmd_id = [0x40, 0x62]
        speed = int16Tolist(value)
        cmd_data = speed + speed + speed + speed
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_forward(self):
        value = self.widget_mecanum.S_speedX.value()
        if value < 0:
            value = -value
            self.widget_mecanum.S_speedX.setValue(value)
            self.widget_mecanum.input_speedX.setText(str(value))
        self.mecanum_move(value)

    def mecanum_back(self):
        value = self.widget_mecanum.S_speedX.value()
        if value > 0:
            value = -value
            self.widget_mecanum.S_speedX.setValue(value)
            self.widget_mecanum.input_speedX.setText(str(value))
        self.mecanum_move(value)

    def mecanum_pid_read(self):  #读取PID参数
        cmd_id = [0x40, 0x71]
        cmd_data = [0]
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_pid_set(self):  #设置PID参数
        cmd_id = [0x40, 0x72]
        cmd_data = []
        for item in self.pid_inputs:
            cmd_data += floatToList_B(float(item.text()))
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_pid_save(self):  #保存PID参数到本地
        makdir(parent_dir_path + "/appDoc/mecanum/pid")
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/mecanum/pid")
        if filename:
            with open(filename, "w", encoding='utf-8') as f:
                cmd_data = [float(item.text()) for item in self.pid_inputs]
                f.write(str(cmd_data))
                self.fuc_log('保存成功')

    def mecanum_pid_load(self):  #加载本地PID参数
        directory = parent_dir_path + "/appDoc/mecanum/pid"
        makdir(directory)
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', directory)
        if filename:
            with open(filename, "r", encoding='utf-8') as f:
                for line in f.readlines():
                    line = ast.literal_eval(line)
                    str_line = [str(item) for item in line]
                    for i in range(12):
                        self.pid_inputs[i].setText(str_line[i])
            # s = "print('Hello, World!')"
            # eval(s)

    def mecanum_setSpeedXYZ(self):  #设置车体速度
        cmd_id = [0x40, 0x60]
        speedX = float(self.widget_mecanum.S_speedX.value())
        speedY = float(self.widget_mecanum.S_speedY.value())
        speedZ = float(self.widget_mecanum.S_speedZ.value())
        self.widget_mecanum.input_speedX.setText(str(speedX))
        self.widget_mecanum.input_speedY.setText(str(speedY))
        self.widget_mecanum.input_speedZ.setText(str(speedZ))
        speedX = floatToList_B(speedX)
        speedY = floatToList_B(speedY)
        speedZ = floatToList_B(speedZ)
        cmd_data = speedX + speedY + speedZ
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setSpeedXYZ1(self):  #设置车体速度
        cmd_id = [0x40, 0x60]
        speedX = float(self.widget_mecanum.input_speedX.text())
        speedY = float(self.widget_mecanum.input_speedY.text())
        speedZ = float(self.widget_mecanum.input_speedZ.text())
        self.widget_mecanum.S_speedX.setValue(int(speedX))
        self.widget_mecanum.S_speedY.setValue(int(speedY))
        self.widget_mecanum.S_speedZ.setValue(int(speedZ))
        speedX = floatToList_B(speedX)
        speedY = floatToList_B(speedY)
        speedZ = floatToList_B(speedZ)
        cmd_data = speedX + speedY + speedZ
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # AGV手柄
    def toggle_widget_handle(self, checked):
        if checked:
            self.widget_handle.show()
        else:
            self.widget_handle.hide()

    def handle_calibrate_begin(self):
        self.flag_calibrate_scale = 1
        self.widget_handle.line_scale_LXmin.setText(str(2048))
        self.widget_handle.line_scale_LXmax.setText(str(2048))
        self.widget_handle.line_scale_LYmin.setText(str(2048))
        self.widget_handle.line_scale_LYmax.setText(str(2048))
        self.widget_handle.line_scale_RXmin.setText(str(2048))
        self.widget_handle.line_scale_RXmax.setText(str(2048))
        self.widget_handle.line_scale_RYmin.setText(str(2048))
        self.widget_handle.line_scale_RYmax.setText(str(2048))
        cmd_id = [0x00, 0x00]
        cmd_data = []
        self.fuc_serial_send(cmd_id, cmd_data)

    def handle_calibrate_end(self):
        self.flag_calibrate_scale = 0
        lx = int16Tolist(int(self.widget_handle.line_scale_LX.text()))
        ly = int16Tolist(int(self.widget_handle.line_scale_LY.text()))
        rx = int16Tolist(int(self.widget_handle.line_scale_RX.text()))
        ry = int16Tolist(int(self.widget_handle.line_scale_RY.text()))
        cmd_id = [0x00, 0x01]
        cmd_data = lx + ly + rx + ry
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 清洗机
    def fuc_pushRod(self, p1, p2):  #推杆
        cmd_id = [0x50, 0x02]
        cmd_data = [p1, p2]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_scrub(self):  #刷盘
        cmd_id = [0x50, 0x02]
        if self.widget_clean.checkBox_scrub.isChecked():
            cmd_data = [0x01, 0x03]
        else:
            cmd_data = [0x01, 0x04]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_water_valve(self):  #清水阀
        cmd_id = [0x50, 0x03]
        if self.widget_clean.checkBox_water_valve.isChecked():
            cmd_data = [0x02, 0x01]
        else:
            cmd_data = [0x02, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_sewage_valve(self):  #污水阀
        cmd_id = [0x50, 0x03]
        if self.widget_clean.checkBox_sewage_valve.isChecked():
            cmd_data = [0x03, 0x01]
        else:
            cmd_data = [0x03, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_vacuum(self):  #真空电机
        cmd_id = [0x50, 0x04]
        value = self.widget_clean.horizontalSlider_vacuum.value()
        cmd_data = [value]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_vacuum_input(self):  #真空电机
        value = int(self.widget_clean.lineEdit_vacuum.text())
        self.widget_clean.horizontalSlider_vacuum.setValue(value)
        self.fuc_vacuum()

    def fuc_water_pump(self):  #水泵
        cmd_id = [0x50, 0x03]
        value = self.widget_clean.horizontalSlider_water_pump.value()
        cmd_data = [0x01, value]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_water_pump_input(self):  #水泵
        value = int(self.widget_clean.lineEdit_water_pump.text())
        self.widget_clean.horizontalSlider_water_pump.setValue(value)
        self.fuc_water_pump()

    def fuc_clean_move(self, p1):  #运动控制
        cmd_id = [0x40, 0x60]
        lineSpeed = float(self.widget_clean.lineEdit_clean_lineSpeed.text())
        radSpeed = float(self.widget_clean.lineEdit_clean_radSpeed.text())
        if p1 == 0:
            lineSpeed = 0
            radSpeed = 0
        elif p1 == 1:
            lineSpeed = abs(lineSpeed)
            radSpeed = 0
        elif p1 == 2:
            lineSpeed = -abs(lineSpeed)
            radSpeed = 0
        elif p1 == 3:
            lineSpeed = 0
            radSpeed = abs(radSpeed)
        elif p1 == 4:
            lineSpeed = 0
            radSpeed = -abs(radSpeed)

        cmd_data = floatToList(lineSpeed) + floatToList(radSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 清洗机工作站
    def clean_output_control(self, outputId):  # 输出控制
        if outputId == 0x01:
            cmd = self.widget_clean.checkBox_station_led_charge.isChecked()
        elif outputId == 0x02:
            cmd = self.widget_clean.checkBox_station_led_water.isChecked()
        elif outputId == 0x03:
            cmd = self.widget_clean.checkBox_station_led_water2.isChecked()
        elif outputId == 0x04:
            cmd = self.widget_clean.checkBox_station_led_clean.isChecked()
        elif outputId == 0x05:
            cmd = self.widget_clean.checkBox_station_led_green.isChecked()
        elif outputId == 0x06:
            cmd = self.widget_clean.checkBox_station_led_red.isChecked()
        elif outputId == 0x07:
            cmd = self.widget_clean.checkBox_station_led_yellow.isChecked()
        elif outputId == 0x08:
            cmd = self.widget_clean.checkBox_station_out_extend.isChecked()
        elif outputId == 0x09:
            cmd = self.widget_clean.checkBox_station_out_retract.isChecked()
        elif outputId == 0x0A:
            cmd = self.widget_clean.checkBox_station_valve_clean.isChecked()
        elif outputId == 0x0B:
            cmd = self.widget_clean.checkBox_station_valve_water.isChecked()
        if cmd:
            cmd = 0x01
        else:
            cmd = 0x00

        cmd_id = [0x41, 0x00]
        cmd_data = [outputId, cmd]
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # CTU
    def ctu_motor_mode(self, motorId):  #电机模式设置
        if motorId == 0x02:
            mode = self.widget_ctu_up.com_motor2_mode.currentText()
        elif motorId == 0x03:
            mode = self.widget_ctu_up.com_motor3_mode.currentText()
        elif motorId == 0x04:
            mode = self.widget_ctu_up.com_motor4_mode.currentText()
        elif motorId == 0x05:
            mode = self.widget_ctu_up.com_motor5_mode.currentText()
        mode = int(mode.split(':')[0])
        cmd_id = [0x40, 0x12]
        if mode < 0: mode = 0x100 + mode
        cmd_data = [motorId, mode]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_motor_setOrigin(self, motorId):  #原点标定
        cmd_id = [0x40, 0x13]
        cmd_data = [motorId]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_motor_position_T(self, motorId):  #电机运行到指定位置
        limitspeed = 3000
        if motorId == 0x02:
            speed = self.widget_ctu_up.input_motor2_speed_T.text()
            position = self.widget_ctu_up.input_motor2_position_T.text()
        elif motorId == 0x03:
            speed = self.widget_ctu_up.input_motor3_speed_T.text()
            position = self.widget_ctu_up.input_motor3_position_T.text()
        elif motorId == 0x04:
            speed = self.widget_ctu_up.input_motor4_speed_T.text()
            position = self.widget_ctu_up.input_motor4_position_T.text()
        elif motorId == 0x05:
            speed = self.widget_ctu_up.input_motor5_speed_T.text()
            position = self.widget_ctu_up.input_motor5_position_T.text()
        speed = int(speed)
        position = int(position)
        if speed > limitspeed:
            speed = limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(limitspeed))
        elif speed < -limitspeed:
            speed = -limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(-limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(-limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(-limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(-limitspeed))

        cmd_id = [0x40, 0x14]
        cmd_data = [motorId] + int32Tolist(position) + int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_device_location_T(self, motorId):  #设备运行到指定位置
        #获取设置参数
        if motorId == 0x02:
            location = self.widget_ctu_up.input_motor2_location_T.text()
            speed = self.widget_ctu_up.input_motor2_speed_T.text()
            position = self.widget_ctu_up.input_motor2_position_T.text()
        elif motorId == 0x03:
            location = self.widget_ctu_up.input_motor3_location_T.text()
            speed = self.widget_ctu_up.input_motor3_speed_T.text()
            position = self.widget_ctu_up.input_motor3_position_T.text()
        elif motorId == 0x04:
            location = self.widget_ctu_up.input_motor4_location_T.text()
            speed = self.widget_ctu_up.input_motor4_speed_T.text()
            position = self.widget_ctu_up.input_motor4_position_T.text()
        elif motorId == 0x05:
            location = self.widget_ctu_up.input_motor5_location_T.text()
            speed = self.widget_ctu_up.input_motor5_speed_T.text()
            position = self.widget_ctu_up.input_motor5_position_T.text()
        location = float(location)
        speed = int(speed)
        position = int(position)
        #速度限制
        limitspeed = 3000
        if speed > limitspeed:
            speed = limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(limitspeed))
        elif speed < -limitspeed:
            speed = -limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(-limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(-limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(-limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(-limitspeed))
        #位置限制
        if motorId == 0x02:
            position = (location - 411) * (-7213.93)  #上升1mm对应编码值递减7213.93，首层离地面411mm,层高为402mm
            position = clip_float(position, -3000 * 7213.93, 0)
        elif motorId == 0x03:
            position = -location / 90 * position
            position = clip_float(position, -3440000, 3440000)
        elif motorId == 0x04:
            position = (location - 675) / 675 * position  #伸到位1350mm,缩到位0mm,(1350-0)/2=675mm
            position = clip_float(position, -1239000, 1239000)
        elif motorId == 0x05:
            position = -(location - 401) / 74 * position  #张开到位475mm,闭合到位328mm,(475-328)/2=74mm
            position = clip_float(position, -161650, 161650)

        position = int(position)
        cmd_id = [0x40, 0x14]
        cmd_data = [motorId] + int32Tolist(position) + int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_motor_speed_T(self, motorId, btnId):  #电机速度设置
        limitspeed = 3000
        if motorId == 0x02:
            speed = self.widget_ctu_up.input_motor2_speed_T.text()
        elif motorId == 0x03:
            speed = self.widget_ctu_up.input_motor3_speed_T.text()
        elif motorId == 0x04:
            speed = self.widget_ctu_up.input_motor4_speed_T.text()
        elif motorId == 0x05:
            speed = self.widget_ctu_up.input_motor5_speed_T.text()
        elif motorId == 0x06:
            speed = 0
        speed = int(speed)
        if speed > limitspeed:
            speed = limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(limitspeed))
        elif speed < -limitspeed:
            speed = -limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(-limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(-limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(-limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(-limitspeed))
        if btnId == 0x01:
            speed = abs(speed)
        elif btnId == 0x02:
            speed = -abs(speed)
        elif btnId == 0x00:
            speed = 0

        cmd_id = [0x40, 0x15]
        cmd_data = [motorId] + int32Tolist(speed * 17895)
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_motor_control(self, motorId, btnId):  #电机一键控制
        if motorId == 0x02:
            mode = self.widget_ctu_up.com_motor2_mode.currentText()
        elif motorId == 0x03:
            mode = self.widget_ctu_up.com_motor3_mode.currentText()
        elif motorId == 0x04:
            mode = self.widget_ctu_up.com_motor4_mode.currentText()
        elif motorId == 0x05:
            mode = self.widget_ctu_up.com_motor5_mode.currentText()
        mode = int(mode.split(':')[0])

        if mode == 0x01 and btnId != 0x00:  #位置模式
            if motorId == 0x02:
                speed = self.widget_ctu_up.input_motor2_speed_T.text()
                position = self.widget_ctu_up.input_motor2_position.text()
            elif motorId == 0x03:
                speed = self.widget_ctu_up.input_motor3_speed_T.text()
                position = self.widget_ctu_up.input_motor3_position_T.text()
            elif motorId == 0x04:
                speed = self.widget_ctu_up.input_motor4_speed_T.text()
                position = self.widget_ctu_up.input_motor4_position_T.text()
            elif motorId == 0x05:
                speed = self.widget_ctu_up.input_motor5_speed_T.text()
                position = self.widget_ctu_up.input_motor5_position_T.text()
            speed = int(speed)
            if motorId == 0x02:  #升降机构
                if (btnId == 0x01):
                    hieght = abs(int(self.widget_ctu_up.input_motor2_location_T.text()))
                    position = int(position) + int(hieght * (-7213.93))  #上升1mm对应编码值递减7213.93
                elif (btnId == 0x02):
                    hieght = abs(int(self.widget_ctu_up.input_motor2_location_T.text()))
                    position = int(position) - int(hieght * (-7213.93))  #上升1mm对应编码值递减7213.93
            else:  #其它机构
                if (btnId == 0x01):
                    position = int(position)
                elif (btnId == 0x02):
                    position = -int(position)
            #速度限制
            limitspeed = 3000
            if speed > limitspeed:
                speed = limitspeed
                if motorId == 0x02:
                    self.widget_ctu_up.input_motor2_speed_T.setText(str(limitspeed))
                elif motorId == 0x03:
                    self.widget_ctu_up.input_motor3_speed_T.setText(str(limitspeed))
                elif motorId == 0x04:
                    self.widget_ctu_up.input_motor4_speed_T.setText(str(limitspeed))
                elif motorId == 0x05:
                    self.widget_ctu_up.input_motor5_speed_T.setText(str(limitspeed))
            elif speed < -limitspeed:
                speed = -limitspeed
                if motorId == 0x02:
                    self.widget_ctu_up.input_motor2_speed_T.setText(str(-limitspeed))
                elif motorId == 0x03:
                    self.widget_ctu_up.input_motor3_speed_T.setText(str(-limitspeed))
                elif motorId == 0x04:
                    self.widget_ctu_up.input_motor4_speed_T.setText(str(-limitspeed))
                elif motorId == 0x05:
                    self.widget_ctu_up.input_motor5_speed_T.setText(str(-limitspeed))

            #位置限制
            if motorId == 0x02:
                position = clip_float(position, -3000 * 7213.93, 0)
            elif motorId == 0x03:
                position = clip_float(position, -3440000, 3440000)
            elif motorId == 0x04:
                position = clip_float(position, -1239000, 1239000)
            elif motorId == 0x05:
                position = clip_float(position, -161650, 161650)

            cmd_id = [0x40, 0x14]
            cmd_data = [motorId] + int32Tolist(position) + int32Tolist(speed * 17895)
            self.fuc_serial_send(cmd_id, cmd_data)
        else:
            self.ctu_motor_speed_T(motorId, btnId)

    def ctu_motor_en(self, motorId):
        if motorId == 0x02:
            en = self.widget_ctu_up.checkBox_en_motor2.isChecked()
        elif motorId == 0x03:
            en = self.widget_ctu_up.checkBox_en_motor3.isChecked()
        elif motorId == 0x04:
            en = self.widget_ctu_up.checkBox_en_motor4.isChecked()
        elif motorId == 0x05:
            en = self.widget_ctu_up.checkBox_en_motor5.isChecked()
        if en:
            en = 0x01
        else:
            en = 0x00
        cmd_id = [0x40, 0x11]
        cmd_data = [motorId, en]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_output_control(self, outputId):  # 输出控制
        if outputId == 0x01:
            cmd = self.widget_ctu_up.checkBox_out_liftPWR.isChecked()
        elif outputId == 0x02:
            cmd = self.widget_ctu_up.checkBox_out_walkPWR.isChecked()
        elif outputId == 0x03:
            cmd = self.widget_ctu_up.checkBox_out_radarF1.isChecked()
        elif outputId == 0x04:
            cmd = self.widget_ctu_up.checkBox_out_radarF2.isChecked()
        elif outputId == 0x05:
            cmd = self.widget_ctu_up.checkBox_out_radarF3.isChecked()
        elif outputId == 0x06:
            cmd = self.widget_ctu_up.checkBox_out_radarF4.isChecked()
        elif outputId == 0x07:
            cmd = self.widget_ctu_up.checkBox_out_radarB1.isChecked()
        elif outputId == 0x08:
            cmd = self.widget_ctu_up.checkBox_out_radarB2.isChecked()
        elif outputId == 0x09:
            cmd = self.widget_ctu_up.checkBox_out_radarB3.isChecked()
        elif outputId == 0x0A:
            cmd = self.widget_ctu_up.checkBox_out_radarB4.isChecked()
        elif outputId == 0x0B:
            cmd = self.widget_ctu_up.checkBox_out_sto.isChecked()
        elif outputId == 0x0D:
            cmd = self.widget_ctu_up.checkBox_out_ledLR.isChecked()
        elif outputId == 0x0E:
            cmd = self.widget_ctu_up.checkBox_out_ledLG.isChecked()
        elif outputId == 0x0F:
            cmd = self.widget_ctu_up.checkBox_out_ledLY.isChecked()
        elif outputId == 0x10:
            cmd = self.widget_ctu_up.checkBox_out_ledRR.isChecked()
        elif outputId == 0x11:
            cmd = self.widget_ctu_up.checkBox_out_ledRG.isChecked()
        elif outputId == 0x12:
            cmd = self.widget_ctu_up.checkBox_out_ledRY.isChecked()

        if cmd:
            cmd = 0x01
        else:
            cmd = 0x00

        cmd_id = [0x41, 0x00]
        cmd_data = [outputId, cmd]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_device_control(self, deviceId):  # 设备控制
        if deviceId == 0x06:
            cmd = self.widget_ctu_up.checkBox_paddle.isChecked()
        if cmd:
            cmd = 0x01
        else:
            cmd = 0x00
        cmd_id = [0x41, 0x01]
        cmd_data = [deviceId, cmd]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_excecute_goods(self):  # 组合控制
        action_type = self.widget_ctu_up.comboBox_action_type.currentIndex() + 1
        location_pick = self.widget_ctu_up.comboBox_location_pick.currentIndex() + 1
        floor_pick = int(self.widget_ctu_up.lineEdit_floor_pick.text())
        location_place = self.widget_ctu_up.comboBox_location_place.currentIndex() + 1
        floor_place = int(self.widget_ctu_up.lineEdit_floor_place.text())
        if floor_pick < 0 or floor_place < 0:  #层数小于0
            return
        elif (location_pick == 1 and floor_pick > 5) or (location_place == 1 and floor_place > 5):  #背篓层数超出
            return
        elif ((location_pick == 2 or location_pick == 3) and floor_pick > 5) or ((location_place == 2 or location_place == 3)
                                                                                 and floor_place > 5):  #货架层数超出
            return
        cmd_id = [0x42, 0x01]
        cmd_data = [action_type, location_pick, floor_pick, location_place, floor_place]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_emergencyStop(self):
        cmd_id = [0x40, 0x61]
        cmd_data = [0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_reset(self):
        cmd_id = [0x40, 0x61]
        cmd_data = [0x02]
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # LED
    def led_num(self):
        num = int(self.widget_leds.lineEdit_led_num.text())
        cmd_id = [0x91, 0x01]
        cmd_data = [0x00, num]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_mode(self):
        mode = self.widget_leds.comboBox_led_mode.currentIndex()
        cmd_id = [0x91, 0x01]
        cmd_data = [0x01, mode]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_color(self):
        color = self.widget_leds.comboBox_led_color.currentIndex()
        cmd_id = [0x91, 0x01]
        cmd_data = [0x02, color]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_light(self):
        light = self.widget_leds.horizontalSlider_led_light.value()
        self.widget_leds.lineEdit_led_light.setText(str(light))
        cmd_id = [0x91, 0x01]
        cmd_data = [0x03, light]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_light_input(self):
        light = int(self.widget_leds.lineEdit_led_light.text())
        self.widget_leds.horizontalSlider_led_light.setValue(light)
        self.led_light()

    def led_speed(self):
        speed = self.widget_leds.horizontalSlider_led_speed.value()
        self.widget_leds.lineEdit_led_speed.setText(str(speed))
        cmd_id = [0x91, 0x01]
        cmd_data = [0x04, speed]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_speed_input(self):
        speed = int(self.widget_leds.lineEdit_led_speed.text())
        self.widget_leds.horizontalSlider_led_speed.setValue(speed)
        self.led_speed()

    #endregion
    #region # 加密解密
    def pad(self, text):
        while len(text) % 16 != 0:
            text += self.PADDING
        return text

    def unpad(self, padded_text):
        return padded_text.rstrip(self.PADDING)

    def encrypt(self, message, key):
        # 生成随机初始化向量 (IV)
        iv = get_random_bytes(16)

        # 对明文进行填充
        cipher_text = self.pad(message.encode('utf-8'))

        # 创建AES对象并加密
        cipher = AES.new(key, self.MODE, iv)
        encrypted_text = cipher.encrypt(cipher_text)

        # 将IV和加密后的数据一起返回
        return base64.b64encode(iv + encrypted_text)

    def decrypt(self, ciphertext_base64, key):
        # 解码Base64编码的密文
        ciphertext = base64.b64decode(ciphertext_base64)

        # 分离出IV和加密文本
        iv = ciphertext[:16]
        cipher_text = ciphertext[16:]

        # 创建AES对象并解密
        cipher = AES.new(key, self.MODE, iv)
        decrypted_text = cipher.decrypt(cipher_text)

        # 去除填充内容
        original_message = self.unpad(decrypted_text).decode('utf-8')

        return original_message

    def fuc_query_uid(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x04, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_generate_lic(self):
        rawlic = self.input_license_xid1.text() + self.input_license_xid2.text() + self.input_license_xid3.text() + self.input_license_xdate.text()
        # 定义一个密钥（必须为16、24或32字节长）
        self.key = get_random_bytes(16)
        # 加密消息
        lic = self.encrypt(rawlic, self.key)
        self.input_license.setText(lic.decode('utf-8'))

    def fuc_authorization(self):
        str_xid1 = self.input_license_xid1.text()
        str_xid2 = self.input_license_xid2.text()
        str_xid3 = self.input_license_xid3.text()
        int_xid1 = listToUint32(bytes.fromhex(str_xid1)) - 0xB9FEC0FB
        int_xid2 = listToUint32(bytes.fromhex(str_xid2)) - 0xC2B7D1C7
        int_xid3 = listToUint32(bytes.fromhex(str_xid3)) - 0x686C6C79
        if int_xid1 < 0: int_xid1 += 0x100000000
        if int_xid2 < 0: int_xid2 += 0x100000000
        if int_xid3 < 0: int_xid3 += 0x100000000
        xid1 = int_xid1.to_bytes(4, byteorder='big')
        xid2 = int_xid2.to_bytes(4, byteorder='big')
        xid3 = int_xid3.to_bytes(4, byteorder='big')
        # self.input_license.setText(xid1.hex()+''+xid2.hex()+''+xid3.hex())

        # 解密消息
        decrypted = self.decrypt(self.input_license.text().encode('utf-8'), self.key)
        print("Decrypted: ", decrypted)
        self.input_license.setText(decrypted)
        print(bytes.fromhex(decrypted))
        cmd_id = [0x10, 0x00]
        xid = [b for b in bytes.fromhex(decrypted)]
        xid1 = xid[0:4][::-1]
        xid2 = xid[4:8][::-1]
        xid3 = xid[8:12][::-1]
        xdate = xid[12:16][::-1]
        cmd_data = [0x07] + xid1 + xid2 + xid3 + xdate
        print(cmd_data)
        self.fuc_serial_send(cmd_id, cmd_data)

    def on_date_clicked(self, date):
        print("hlly")
        str_date = date.toString("yyMMdd")
        self.input_license_date.setText(str_date)
        str_date = hex(date.year() - 2000)[2:].zfill(2) + hex(date.month())[2:].zfill(2) + hex(date.day())[2:].zfill(2)
        print(str_date)
        int_xdate = listToUint32(bytes.fromhex(str_date)) + 0x686C6C79
        self.input_license_xdate.setText(bytes(int32Tolist_big(int_xdate)).hex())

    #endregion
    #region # 语音播报
    def fuc_voice_play(self):
        cmd_id = [0x40, 0x03]
        voice = self.com_voiceList.currentIndex() + 1
        cmd_data = [voice, 0xff, 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_voice_addFile(self):
        print("hlly")
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', r'E:\project\06 标准机开发\4、调试\语音素材包')
        dirname = os.path.dirname(filename)
        filename = os.path.basename(filename)
        self.root_mp3_files = dirname
        self.list_voice.addItem(filename)

    def fuc_voice_addFiles(self):
        filePath = self.input_voice_filePath.text()
        for root, dirs, files in os.walk(filePath):
            self.root_mp3_files = root
            for file in files:
                if file.endswith('.mp3'):
                    self.list_voice.addItem(file)
        # self.listModel = QStringListModel()
        # self.listModel.setStringList(self.mp3_files)
        # self.listModel.sort(0, Qt.AscendingOrder)#DescendingOrder
        # self.list_voice.setModel(self.listModel)
        # self.mp3_files.append(os.path.join(root, file))
    def fuc_voice_moveUp(self):
        current_row = self.list_voice.currentRow()
        # current_item=self.list_voice.item(current_row)
        if current_row > 0:
            item_above = self.list_voice.takeItem(current_row - 1)  # 取出上一行的条目
            self.list_voice.insertItem(current_row, item_above)  # 将原本上一行的条目放到当前条目的下方

    def fuc_voice_moveDown(self):
        current_row = self.list_voice.currentRow()
        if current_row < self.list_voice.count() - 1:
            item_below = self.list_voice.takeItem(current_row + 1)
            self.list_voice.insertItem(current_row, item_below)

    def fuc_voice_delFile(self):
        fileindex = self.list_voice.currentRow()
        self.list_voice.takeItem(fileindex)

    def fuc_voice_delFiles(self):
        self.list_voice.clear()

    def fuc_voice_downloadSel(self):  #下载选中的音频
        self.flag_voice_download = 1
        self.progress_voice_file.setMaximum(1)
        fileindex = self.list_voice.currentRow() + 1
        item_text = self.list_voice.currentItem().text()
        filename = (os.path.join(self.root_mp3_files, item_text))
        self.thread_sendAudioFile = thread_sendAudioFile(filename, fileindex, 0)
        self.thread_sendAudioFile.sig_sendAudioFile.connect(self.fuc_voice_sendFile)
        self.thread_sendAudioFile.sig_log.connect(self.fuc_log)
        self.thread_sendAudioFile.sig_packNum.connect(self.fuc_voice_downloadCnt)
        self.thread_sendAudioFile.sig_done.connect(self.fuc_voice_downloadOnce)
        self.thread_sendAudioFile.start()

    def fuc_voice_downloadCnt(self, packNum):
        self.progress_voice_pack.setMaximum(packNum)

    def fuc_voice_downloadFeedback(self, data):
        if len(data) == 7:
            if data[0] == 0x7E and data[1] == 0xA0 and data[6] == 0xEF:
                self.progress_voice_pack.setValue(data[4] * 256 + data[5])
        elif len(data) > 7 and data[0] == 0x7E and data[1] == 0xA0 and data[6] == 0xEF:
            self.progress_voice_pack.setValue(data[4] * 256 + data[5])
        else:
            a = HexStrAddSpace(data.hex())
            self.fuc_log(a)

    def fuc_voice_downloadDone(self):  #音频下载完成后
        self.fuc_log("下载完成")
        self.flag_voice_download = 0
        time.sleep(0.1)
        sendData = [0x7E, 0xA3, 0x00, 0x02, 0x00, 0x00, 0xEF]
        self.fuc_serial_sendRaw(sendData)

    def fuc_voice_downloadOnce(self):  #选中的音频下载完成后
        progress = self.progress_voice_pack.value()
        progress_total = self.progress_voice_pack.maximum()
        print(progress)
        print(progress_total)
        if progress == progress_total:
            self.fuc_voice_downloadDone()
        else:
            print("下载失败")
            self.fuc_log("下载失败")
        self.progress_voice_file.setValue(1)

    def fuc_voice_downloadNext(self):  #下载下一个音频
        progress = self.progress_voice_pack.value()
        progress_total = self.progress_voice_pack.maximum()
        print(progress)
        print(progress_total)
        if progress >= progress_total - 1:
            self.list_voice_index += 1
            self.progress_voice_file.setValue(self.list_voice_index)
            if self.list_voice_index < self.list_voice_num:
                item_text = self.list_voice.item(self.list_voice_index).text()
                filename = (os.path.join(self.root_mp3_files, item_text))
                self.thread_sendAudioFile = thread_sendAudioFile(filename, self.list_voice_index + 1, 1)
                self.thread_sendAudioFile.sig_sendAudioFile.connect(self.fuc_voice_sendFile)
                self.thread_sendAudioFile.sig_log.connect(self.fuc_log)
                self.thread_sendAudioFile.sig_packNum.connect(self.fuc_voice_downloadCnt)
                self.thread_sendAudioFile.sig_next.connect(self.fuc_voice_downloadNext)
                self.thread_sendAudioFile.start()
            else:
                self.fuc_voice_downloadDone()
        else:
            print("下载失败")
            self.fuc_log("下载失败")

    def fuc_voice_downloadAll(self):  #下载所有的音频
        self.flag_voice_download = 1
        self.list_voice_num = self.list_voice.count()
        self.progress_voice_file.setMaximum(self.list_voice_num)
        self.progress_voice_file.setValue(0)
        self.list_voice_index = 0
        item_text = self.list_voice.item(0).text()
        filename = (os.path.join(self.root_mp3_files, item_text))
        self.thread_sendAudioFile = thread_sendAudioFile(filename, self.list_voice_index + 1, 1)
        self.thread_sendAudioFile.sig_sendAudioFile.connect(self.fuc_voice_sendFile)
        self.thread_sendAudioFile.sig_log.connect(self.fuc_log)
        self.thread_sendAudioFile.sig_packNum.connect(self.fuc_voice_downloadCnt)
        self.thread_sendAudioFile.sig_next.connect(self.fuc_voice_downloadNext)
        self.thread_sendAudioFile.start()

    def fuc_voice_sendFile(self, sendData):
        result = self.fuc_serial_sendRaw(sendData)
        if (result == 0): self.thread_sendAudioFile.stop_send()

    def fuc_voice_update(self):
        cmd_id = [0x40, 0x02]
        cmd_data = [0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_voice_exit(self):
        sendData = [0x7E, 0xA3, 0x00, 0x02, 0x00, 0x00, 0xEF]
        result = self.fuc_serial_sendRaw(sendData)
        # print(result)
        self.flag_voice_download = 0
        time.sleep(0.1)
        sendData = [0xAA, 0xAA, 0xAA]
        result = self.fuc_serial_sendRaw(sendData)

    def fuc_voice_setVolume(self):
        cmd_id = [0x40, 0x03]
        volume = self.slider_volume.value()
        cmd_data = [0xff, 0xff, volume]
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 输入输出
    def fuc_outputControl(self):
        out = [0x00] * 3
        out[0] = self.check_out_1.isChecked() << 7 | self.check_out_2.isChecked() << 6 | self.check_out_3.isChecked(
        ) << 5 | self.check_out_4.isChecked() << 4 | self.check_out_5.isChecked() << 3 | self.check_out_6.isChecked(
        ) << 2 | self.check_out_7.isChecked() << 1 | self.check_out_8.isChecked()
        out[1] = self.check_out_9.isChecked() << 7 | self.check_out_10.isChecked() << 6 | self.check_out_11.isChecked(
        ) << 5 | self.check_out_12.isChecked() << 4 | self.check_out_13.isChecked() << 3 | self.check_out_14.isChecked(
        ) << 2 | self.check_out_15.isChecked() << 1 | self.check_out_16.isChecked()
        out[2] = self.check_out_17.isChecked() << 7 | self.check_out_18.isChecked() << 6 | self.check_out_19.isChecked(
        ) << 5 | self.check_out_20.isChecked() << 4 | self.check_out_21.isChecked() << 3 | self.check_out_22.isChecked(
        ) << 2 | self.check_out_23.isChecked() << 1 | self.check_out_24.isChecked()
        # str_out=[hex(i) for i in out]
        # print(str_out)
        cmd_id = [0x40, 0x08]
        cmd_data = out
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 清洗机驱动板测试
    def fuc_driver_pwm(self):
        num = self.com_driver_output.currentIndex() + 1
        pwm = self.slider_driver_pwm.value()
        self.lineEdit_driver_pwm.setText(str(pwm))
        cmd_id = [0x42, 0x00]
        cmd_data = [num, pwm]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_driver_speed(self):
        num = self.com_driver_motor.currentIndex() + 1
        dir = self.com_driver_dir.currentIndex()
        if dir == 0:
            dir = 0x01
        elif dir == 1:
            dir = 0x02
        else:
            dir = 0x00
        speed = self.slider_driver_speed.value()
        self.lineEdit_driver_speed.setText(str(speed))
        cmd_id = [0x42, 0x01]
        cmd_data = [num, dir, speed]
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 程序监控
    def fuc_print_threadName(self):
        cmd_id = [0x80, 0x03]
        if self.checkBox_print_threadName.isChecked():
            cmd_data = [0x01]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_query_softwareVersion(self):
        cmd_id = [0x80, 0x04]
        cmd_data = []
        self.fuc_serial_send(cmd_id, cmd_data)

    #endregion
    #region # 上位机启动
    def restart(self):
        self.close()
        qApp.exit(current_exit_code)

    def showChild(self):  #上位机启动时显示子窗口
        if self.widget_config.p_isShowMecanum.isChecked():
            self.widget_mecanum.show()
        if self.widget_config.p_isShowHandle.isChecked():
            self.widget_handle.show()
        if self.widget_config.p_isShowDebug.isChecked():
            self.window_debug.gridLayout.addWidget(self.paint)
            self.paint.setMinimumWidth(500)  #设置widget最小宽度
            self.window_debug.show()

    #endregion


if __name__ == '__main__':
    current_exit_code = 1002457844
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)  # 适应高分辨率
    app = QApplication(sys.argv)
    # app.setStyle('Fusion')  # 设置 Fusion 风格
    # 获取当前 exe 所在文件夹路径
    parent_dir_path = get_exe_directory()
    # parent_dir_path = os.path.dirname(get_exe_directory())
    os.chdir(parent_dir_path)
    # current_directory = get_exe_directory()
    # widget_picture = window_picture()
    # widget_picture.show()
    # sys.exit(app.exec())
    while True:
        main_window = Window_main()
        # widget_picture.hide()
        # style_file = r'E:\python\上位机_舜宇贝尔\AGV-Develop_V3.4\QSS/myQSS.qss'
        # style_sheet = QSSLoader.read_qss_file(style_file)
        # print(style_sheet)

        # app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyqt5',palette=LightPalette()))#灰色主题
        # app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyqt5'))#暗黑色主题

        main_window.show()
        end_time = time.time()
        elapsed_time = end_time - start_time
        print("窗口加载时间：", elapsed_time, "秒")
        main_window.showChild()
        code = app.exec()
        if code != current_exit_code:
            if global_flag_client == 1:
                global_flag_client = 0
                global_s.close()
                print("socket已断开")
            break
    print("程序关闭")
    sys.exit(0)
